From: Jan Kratochvil
Date: Tue, 9 Jan 2024 00:24:07 +0000 (+0800)
Subject: openpgp: 7E25094870599135A83DD2C6C446E92ECE0D66B2
X-Git-Url: http://git.jankratochvil.net/?p=www.jankratochvil.net.git;a=commitdiff_plain;h=HEAD;hp=24a8daf8b7d70742e17efb0480135c28c71ab645
openpgp: 7E25094870599135A83DD2C6C446E92ECE0D66B2
---
diff --git a/WebConfig.pm b/WebConfig.pm
index 514da55..44fe78f 100644
--- a/WebConfig.pm
+++ b/WebConfig.pm
@@ -50,11 +50,11 @@ our %WebConfig=(
},
# "viewcvs_My"=>"http://cvs.jankratochvil.net/viewcvs/MyWeb/",
# "viewcvs"=>"http://cvs.jankratochvil.net/viewcvs/www/www.jankratochvil.net/",
- "viewcvs_My"=>"http://git.jankratochvil.net/?p=MyWeb.git;a=blob;hb=HEAD;f=",
- "viewcvs"=>"http://git.jankratochvil.net/?p=www.jankratochvil.net.git;a=blob;hb=HEAD;f=",
+ "viewcvs_My"=>"//git.jankratochvil.net/?p=MyWeb.git;a=blob;hb=HEAD;f=",
+ "viewcvs"=>"//git.jankratochvil.net/?p=www.jankratochvil.net.git;a=blob;hb=HEAD;f=",
"title_prefix"=>"Jan Kratochvil",
# "project_viewcvs"=>"http://cvs.jankratochvil.net/viewcvs/",
- "project_viewcvs"=>"http://git.jankratochvil.net/",
+ "project_viewcvs"=>"//git.jankratochvil.net/",
# "pserver"=>':pserver:pserver:@cvs.jankratochvil.net',
# "pserver_path"=>"/cvs",
"resume_url"=>$resume_url,
@@ -81,7 +81,7 @@ our %WebConfig=(
my @sections=(
"/project/"=>"Projects",
"/product/"=>"Products",
- "http://git.jankratochvil.net/"=>"GIT",
+ "//git.jankratochvil.net/"=>"GIT",
# "/News.pm"=>"News",
$resume_url=>"Resume",
"/Contact.pm"=>"Contact",
diff --git a/pgp-JanKratochvil.txt b/pgp-JanKratochvil.txt
index cf32c33..7a9a7ed 100644
--- a/pgp-JanKratochvil.txt
+++ b/pgp-JanKratochvil.txt
@@ -1,30 +1,51 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
-mQENBFwyNHIBCAC1P4df+lz1OCnbxm5Jg5QK2T/JbcmvHZ6J3dy5pKe/d8HnD6kQ
-OFiNe7bfurUcDFm8Lst5WNALviCzn+exetCFhtTN4Bnf9VupnBfKI/Sh32D6glv1
-BIas4+dzYnArFklMvCnHilq6/JykOZeTmAkRorakzDlfQfvQd52yENrJG2qz9WRV
-8jIdpjrY42G0dolZHSusgsvbyRwzYNZaF/D0Zv0BTt/qWpL1lbY3/EZ845/AoLLH
-7S9W7TMQ35swRHMaWttsTTer32sGMxOIC/j4gELHu3gax/4pC8W3Yen7v+9Q1YSp
-7jYjK/BnO/3LeiEM6UdrGztEqdu6es1LtsqfABEBAAG0JkphbiBLcmF0b2Nodmls
-IDxqYW5AamFua3JhdG9jaHZpbC5uZXQ+iQFUBBMBCAA+AhsDBQsJCAcCBhUKCQgL
-AgQWAgMBAh4BAheAFiEE3yq9weZy4rKLvsxtEEZ8LIpIxqwFAlwyNRcFCQlmAiUA
-CgkQEEZ8LIpIxqwQxgf/cWoDzhDNZwKvrw4KpBG8ji4h49Gh3f+nJRcJpIBF3W/h
-n3G0fNqqWXT7nLulyELMV+Hlk0HOVn7s76cCIMaClVmoi2nLs+mFRW5FS+64CBUy
-rssdgO6Y572l2UCOooUQxx6brbC+wzYPdANWerwj+s43pPR+F9TYLBoZq7LAxZ6m
-+cniyrFkRy8sT8jC57YssFULsqlv1o/IaV9Q5mqbvGh6CrAGXoq4/sL79nDREWIw
-PIh4AvYcXwRQ7x6rkCwo9ZMIau/KKXuvomdLaCTBJMZpOxmb5NlRENAOyp6zASn/
-ibQKrOCvjRPjqzjDvHbHH4oOX7+HI8SGkZXc7Bx7OrkBDQRcMjRyAQgA1yoLJ/Wz
-erQVbmntDI9kjhHu5UHvLinrE6ozYUj+dSYMfideTSVKQ5dhZKzz50QrUHE8NwVv
-6fy9TmUzQJqi4jLzGaNQqyjJleHAECThJeRUlz9Qlxw+zCuS/W3i6QPwqBgWns9y
-TS7mqjuufe52Fgmj5PNvZfbmMTKjv/rlvHLQ95rbGTLhu3EeG4lrB1lloSfi4lZ0
-wHMi50y3o/tgNtLRBl6Y9N3lSlOJ61qnWsEOuQB7Uf19uj3T8hWrgIzZPNJ6cQNE
-mpsblMtUGLaJU1y+cHT2ByvGGSqq2vF2KmFhduxtkFfYSuR68ntqxcbe++T4VdOI
-6N54ReC6ohWv4wARAQABiQE8BBgBCAAmAhsMFiEE3yq9weZy4rKLvsxtEEZ8LIpI
-xqwFAlwyNaEFCQlmAq8ACgkQEEZ8LIpIxqztxAgAgza8ZJZbIGYQp16VBgKlA8Xq
-IuW2PsREJYl5JT2UuwGm03IOmjspLL0f+g7dNXlDruC8v2TrSF2H4lmJRnIOywkx
-evE8DKnyvF9T16aYVS2UgV6B3i0JViot1v5jNMMAOu0EtPxD3ZaRMlBR/JzpuOsM
-lFL5ZYMQyYWiF6hO764WhQdaqfiHQyS2RDGdPr6VhK6KcMzJNDt+tAIuyIA8pdU0
-t8XUa1Ya7Mn+G+pyyotqckis7LWlSmV8fIuPZ3fH33ObDk1BMvj18qMBE6U9cuf3
-mofXmTtZhIfdpdREHqmoxuI8MzuijWsopvCiZg1sD1R+Geczpn5GAY2Q0u3Kgw==
-=PjgA
+mQINBFpwqN8BEADedUDuF0KlM59QAmThovidEXGklhtBeg0dGYpBoaiJDVs7QYdJ
+3fGH0fRKsgnrlUSDUAbRd7MwfClRHPMc1nfI6maNHF2tsv4+bopL5mRXcL+OB2iB
+FNtydVMll9tyEuhi5umgM7YoySTrI077IIgSZ6vutTPf9XqjhTx0Z3tWt5ETBKjL
+VNCpEgRHaMAe36CCPLpCiAY+aZJx2r9peK1l2WrsQQPzWIm942ysCQD2gZ2sEwiN
+WBDfXwhODG7TW6DhLBjC7bKnxrboWjyS244DVwjiAxDugxEBtwQlfmo2z98URjln
+naScT7ZqqDfoa2jrzX7zSm3DdXe0I1I7yTeiLWD1tbI1MGzhx3S9qQE7zOKnX951
+aE1V4Nk4O1Aw/l7LEfin/pmqVB7grxj4aGo9ifn++/bcavSqPg+bP490pblqXD+B
+r2E1reS1M1KW+PWChi/08W3fcf3K4wx7WaIs6TsD8zKe1L1i/RV8dcggDOUEc3n6
+3ikZ2BvPEBAkxaYIJhuSypfw4sVxqOldf2bHpJRtxZqFOtTbuw4L20LFZtOrnQjX
+j3sTgu1tcPWLj+Gxp3bdRxCKZm4yZ4DKPGnReezMg+dMG/md+sGVEhkO2JCp2XHW
+wNHld4R5NXU3NqBtscwi9ixaIqhm+kpB5Ix+ssNoZISYm41AZMoA6oeIpQARAQAB
+tCZKYW4gS3JhdG9jaHZpbCA8amFuQGphbmtyYXRvY2h2aWwubmV0PokCPgQTAQIA
+KAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AFAlyIF1IFCRTjcWwACgkQxEbp
+Ls4NZrK3DxAAypzW+Amcwxfw/IWxdeCtPaZ3fGkGfnszG9GM8OoVM4HuG9qIkIkn
+3pz7FcQ7naowPaPt8/hm/Hsti3okaAtSuCXwpw7GK/S98fHKHsxgcL00wG0Wa8YE
+MMQsajTBtYV3CkNJDhFGG1mKR+OtozTJHa/mmTACKsleEss/oe8/oxCWr2NhiPTi
+Rqj4vDgMD6OWePuIPymBCau9lDxuyXiDBFuMdunCJRkngWvSp4F6DTBrDIXztjfY
+JeQzV7yS6YSDdHD+DdLfcSM0HYmb9AjOHwHmHRuvVtwiy3lqzGnl5/LZzWLk/f0o
+bOFUDz/YWnhtGZ4GbY+vEvg7Cz7+oGr5EHe3m7ifs7spWSaWSp5cV8yEc2WUUL9y
+sjsAItSMLvzEQsAoy28hzcW12GpbUReVt5ZgjY7EPrb6CmA0wdpprCI6w8zD9GgE
+LT6/I3Wkr8Uu/If93/wsIw/USHZpp3viozD8BoY4Y7O0gPPk8TbMFOjmh/hEd9Nf
+WR6LiVUhPV8ScUYXbLQUdFmmvfZK0TV8+w6MRCi11UVKQr3lH3skoZzhKzNOsy4f
+qbWCoMprdpkGSL7WAhngsT4dRe8mRV0WTtv9NUNvkzD5S01cvmCWeTgYeiLILldU
+tI6CZskuJQvE3wnmW3aE/laqpmawJcNSfsuB/NGXVPRK1usHghte6C25Ag0EWnCo
+3wEQANaKI/e6frHRZRAwwI+5XBNwoSStLsaXz4EbvM5CVyV7R7dZvooHqq6gvJgb
+naf9xhDHwUV70sILVD4Qs264r7uwWT8MTgH/G1kz3vWlnMdJnGn1LnLxJWxobAT7
+SuVrBUGIfQw7bd6lmOBXLtGt3DHEXGVUpaHxRO4Oo2dNdyGm3/+3YUJ7TO7TG6dc
+dEF2q5Bk6vzUN8erXUeoNyidjA1oI/+AiYy2QlNOF/lpoVp4oWI9ffAXKa6+S7nM
+3QinR3/jgtvRe8LDQpQMfQ5bWZxEUyZhz+s/ejgUgO2BbVi2l2PfsQe0TlNQREZy
+OewrfqDvwktPG6xkC5MwMyM46NwAzFM0Dpe8W471lk8FpKLaABELMgrmE+bbgi33
+h/lfcF1pVFEUGwBtpLpXsvfff5EWcEsjaAsyAUlRYiCBf8Y9VoECJyA1SfDxyXjM
+ycC088eCpH58IHiDZYluadFHRMZ1lwHbv8VVysL9GpgqqMfa8EYIv+93CNmD4mB8
+ecyTuiC8+Enx7d89kgzKUBiy/7/pBYIl2ErtSeDOJ5KI2+1sJQch8BIav1ZtC9I+
+yySQZE3Etst1vklXnnoSRfnqV7ogsSrC3SmIJvkDDFFWtXLbnHsFTF+1gLE/JuYC
+vC8qGHV4JU6T3V1kQxNB4VK2mj/VSXBp64lgAGZwP1pN6BxdABEBAAGJAiUEGAEC
+AA8CGwwFAlyIF4AFCRTjcZwACgkQxEbpLs4NZrJL/RAAzgbXi5iUJfMATzq2L9Ez
+sutNFHX8IGYxsB3ChBCB2ylLLk0UhzT69naOrw3yW87Xzu/V8Ft4GtE5vDt+Oymb
+fIwU8cnIlu8hz8jR16RUZcIby35TSjRlV+KxlGsYOdY1A+K0IZyw/h487AbGMLtZ
+8Grf6GWJAGKBKKXKwwiwmRAsOTh6YynT3ibbiaHaeWdrFJOOJyZSPgy2sMz/4R05
+Ii9MpVJDSe8IGIibRuwG6cNX8BhEanrcciqjUYE/UmaUnm0pDAKw03G7Dsu3vJ6i
+/Bo9CiXhnM0eXvqw8a9hZy/YNJH/kShiWrG9ymUg+jymFb1rr1aVEZv9HTbu1VPn
+IPDWgp8tX+YdURr1OwCEdRq3A3/0uuZrILJPCOGsRtl1IVaYEhkQ7fWLcVCLUGAv
+ItLbaZtjWGjjlXe8Lg1oaybeeYTpoiqC/d7UZss3RsD3EGOE8/gb1r/C/GO3A/qd
+FvXGXuCsJa5p1GnP0zdCm74VjTHTMjw34QJ/3vValgYoJe709B13Y4UtNUiB+YPe
+Jkn2xJg35qNkWksbrTxBY0pbY06Ks1gpz6OHHnzXaINcYX2G8sJc5VQTX8GN3Ntr
+KDXjL18oDwG3j7HUMWculm0x3gyxRYN8b/meCnkJQ4VMEyMcDevWzU3clqgri7EU
+JNO9YOtYh2LJ/itLlpmJGtU=
+=Rvv1
-----END PGP PUBLIC KEY BLOCK-----
diff --git a/project/AutoGen/Index.pm b/project/AutoGen/Index.pm
index 04ebfa1..43032f7 100644
--- a/project/AutoGen/Index.pm
+++ b/project/AutoGen/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"priority"=>540,
"cvs"=>"macros",
"link-Documentation"=>'/project/Pod2Html.pm?cvs=macros/AutoGen.pm',
- "link-Source file"=>'http://git.jankratochvil.net/?p=macros.git;a=blob_plain;hb=HEAD;f=AutoGen.pm',
+ "link-Source file"=>'//git.jankratochvil.net/?p=macros.git;a=blob_plain;hb=HEAD;f=AutoGen.pm',
"summary"=>"autogen.sh while supporting CVS/.rpm/.deb",
"license"=>"GPL",
"maintenance"=>"active",
diff --git a/project/PerlMail/Index.pm b/project/PerlMail/Index.pm
index 11b9d84..f65ffab 100644
--- a/project/PerlMail/Index.pm
+++ b/project/PerlMail/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>640,
"cvs"=>"PerlMail",
- "link-README"=>'http://git.jankratochvil.net/?p=PerlMail.git;a=blob_plain;hb=HEAD;f=README',
+ "link-README"=>'//git.jankratochvil.net/?p=PerlMail.git;a=blob_plain;hb=HEAD;f=README',
"summary"=>sub { return "Perl mail processor - ".a_href('http://www.procmail.org/','procmail')."(1) successor"; },
"license"=>"GPL",
"maintenance"=>"ready",
diff --git a/project/TraceFS/Index.pm b/project/TraceFS/Index.pm
index 277524a..60458de 100644
--- a/project/TraceFS/Index.pm
+++ b/project/TraceFS/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"w32",
"priority"=>610,
"icon"=>"TraceFS-icon.png",
- "link-GIT subtree"=>'http://git.jankratochvil.net/?p=captive.git;a=tree;f=src/TraceFS',
+ "link-GIT subtree"=>'//git.jankratochvil.net/?p=captive.git;a=tree;f=src/TraceFS',
"link-Documentation"=>"/project/captive/doc/CacheManager.pm#TraceFS",
"summary"=>"Microsoft Windows Kernel API Tracer",
"license"=>"GPL",
diff --git a/project/badblock_guess/Index.pm b/project/badblock_guess/Index.pm
index 3eafa9e..b28a8d0 100644
--- a/project/badblock_guess/Index.pm
+++ b/project/badblock_guess/Index.pm
@@ -35,7 +35,7 @@ our @ListItem=(
"download-compiled static binary"=>"badblock-guess",
"download-gzipped compiled static binary"=>"badblock-guess.gz",
"cvs"=>"badblock-guess",
- "link-README"=>'http://git.jankratochvil.net/?p=badblock-guess.git;a=blob_plain;hb=HEAD;f=README',
+ "link-README"=>'//git.jankratochvil.net/?p=badblock-guess.git;a=blob_plain;hb=HEAD;f=README',
"summary"=>"Data recovery from a damaged disk",
"license"=>"GPL",
"maintenance"=>"ready",
diff --git a/project/checkstatic/Index.pm b/project/checkstatic/Index.pm
index 35ed9c7..98cb0d4 100644
--- a/project/checkstatic/Index.pm
+++ b/project/checkstatic/Index.pm
@@ -31,7 +31,7 @@ our @ListItem=(
"name"=>"checkstatic",
"platform"=>"unixdevel",
"priority"=>510,
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/checkstatic',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/checkstatic',
"summary"=>"C sources symbol attributes checker",
"license"=>"PD",
"maintenance"=>"ready",
diff --git a/project/cvsbranchdiff/Index.pm b/project/cvsbranchdiff/Index.pm
index 111567f..1d168e3 100644
--- a/project/cvsbranchdiff/Index.pm
+++ b/project/cvsbranchdiff/Index.pm
@@ -31,7 +31,7 @@ our @ListItem=(
"name"=>"cvsbranchdiff",
"platform"=>"unixdevel",
"priority"=>490,
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/cvsbranchdiff',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/cvsbranchdiff',
"summary"=>"CVS Branching Utility",
"license"=>"PD",
"maintenance"=>"ready",
diff --git a/project/cvsutil/Index.pm b/project/cvsutil/Index.pm
index ae40547..c4b652b 100644
--- a/project/cvsutil/Index.pm
+++ b/project/cvsutil/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"name"=>"cvsutil",
"platform"=>"unixdevel",
"priority"=>500,
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/cvsutil',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/cvsutil',
"summary"=>"Clean CVS checkout working files or safely change CVS/Root",
"license"=>"PD",
"maintenance"=>"merge",
diff --git a/project/fixhtml/Index.pm b/project/fixhtml/Index.pm
index 094454b..888b48f 100644
--- a/project/fixhtml/Index.pm
+++ b/project/fixhtml/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"unixuser",
"trivia"=>1,
"priority"=>20,
- "link-Source file"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/fixhtml',
+ "link-Source file"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/fixhtml',
"summary"=>"Convert HTML files URLs to relative ones",
"license"=>"PD",
"maintenance"=>sub {
diff --git a/project/harpy/Index.pm b/project/harpy/Index.pm
index 620c3fa..9aa7e23 100644
--- a/project/harpy/Index.pm
+++ b/project/harpy/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>486,
"cvs"=>"harpy",
- "link-Source file"=>'http://git.jankratochvil.net/?p=harpy.git;a=blob_plain;hb=HEAD;f=harpy',
+ "link-Source file"=>'//git.jankratochvil.net/?p=harpy.git;a=blob_plain;hb=HEAD;f=harpy',
"summary"=>"ARP spoofer of nonexisting ethernet addresses",
"license"=>"GPL",
"maintenance"=>"ready",
diff --git a/project/inetdmx/Index.pm b/project/inetdmx/Index.pm
index f2052b8..af9a92b 100644
--- a/project/inetdmx/Index.pm
+++ b/project/inetdmx/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>435,
"cvs"=>"inetdmx",
- "link-Source file"=>'http://git.jankratochvil.net/?p=inetdmx.git;a=blob_plain;hb=HEAD;f=inetdmx.c',
+ "link-Source file"=>'//git.jankratochvil.net/?p=inetdmx.git;a=blob_plain;hb=HEAD;f=inetdmx.c',
"summary"=>"xinetd(8)/inetd(8) on-demand servers multiplexor",
"license"=>"GPL",
"maintenance"=>"ready",
diff --git a/project/int13sniff/Index.pm b/project/int13sniff/Index.pm
index e5292c2..45651a9 100644
--- a/project/int13sniff/Index.pm
+++ b/project/int13sniff/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"priority"=>370,
"icon"=>"int13sniff-icon.png",
"summary"=>'Trace PC bootloader disk operations',
- "download-source"=>'http://surprise.cvs.sourceforge.net/viewvc/surprise/surprise/misc/int13sniff.S?content-type=text/plain',
+ "download-source"=>'int13sniff.S',
"download-gzip(1)ped floppy image head"=>'int13sniff.bin.gz',
"link-parent Surprise project"=>"/project/surprise/",
"license"=>"GPL",
diff --git a/project/int13sniff/int13sniff.S b/project/int13sniff/int13sniff.S
new file mode 100644
index 0000000..c82675e
--- /dev/null
+++ b/project/int13sniff/int13sniff.S
@@ -0,0 +1,1019 @@
+/*
+ * misc/int13sniff.S
+ *
+ * Copyright (C) 2000
+ * Partition Surprise Team
+ *
+ * $Id: int13sniff.S,v 1.4 2001/02/13 00:47:18 kratochvil Exp $
+ */
+
+
+/*
+ *
+ * Sniffer for INT13 calls
+ *
+ * Prepare your floppy and reboot from it with 0x80 disk for boot-sniffing:
+ * ANY DATA ON YOUR FLOPPY (/dev/fd0) WILL BE ERASED BY THIS COMMAND!
+ *
+ * make int13sniff.fd0
+ *
+ *
+ */
+
+
+/* !!! All defines below must be COMMENTED-OUT !
+ * !!! Define to value 0 has no effect.
+ */
+
+/* Enable of normal screen dump output.
+ * You can probably never need to undefine it, only if it severly
+ * harms some needed (fullscreen) application display output.
+ */
+#define SCREEN_PRINT 1
+
+/* Enable (if defined) dump output to all serials found
+ */
+#define SERIAL_BAUDRATE 9600
+
+/* Enable to refuse dumping output to port without DSR active
+ * - pro: doesn't get slow by dumping to not-connected ports
+ * - con: some cables may not have DSR (DTR<->DSR wiring would be sufficient)
+ */
+#define SERIAL_REQUIRE_DSR
+
+/* Enable to dump all messages back to floppy
+ * - con: message output is slowed down a bit
+ * WARNING: some BIOSes fail to report geometry (INT 0x13/AH=0x08) for HDD!
+ */
+#define OUTPUT_STORE_DISK 0x00
+
+/* Linear sector offset where to start writing buffer sectors
+ * If undefined, it is calculated to be the last sector of "int13sniff.bin".
+#define OUTPUT_STORE_ADDRESS -1
+ */
+
+/* Stack size to reserve in the top-memory area
+ * WARNING: Due to SECTORS_LEN reading, this value MUST be always >=
+ * than the sector size (0x200)!
+ */
+#define STACKSIZE_TOP 0x200
+
+/* Maintainers: Disable occupying of any (0x13 & 0x18) interrupt vectors
+#define DISABLE_SNIFF 1
+ */
+
+/* Maintainers: Disable .org macroinstructions: ONLY FOR compilation tuning!
+ * - image produced doen't have any functionality!
+#define DISABLE_ORG 1
+ */
+
+
+/* Internal defines */
+#define SERIAL_SENDWAIT 16 /* multiplied by 0x10000, Linux kernel has ~15 (looping 1E6 times) */
+#define SERIAL_LCR_INIT (UART_LCR_WLEN8) /* 8N1, no DLAB */
+#define SERIAL_MCR_INIT (UART_MCR_DTR | UART_MCR_RTS) /* we are RTSed! */
+#define MEM_KBYTES 0x413 /* BIOS variable */
+#define PORTADDR_BASE 0x400 /* BIOS variable */
+#define SECTOR2_MAGIC 0xEFBE
+#define SERIAL_DIVISOR (115200/SERIAL_BAUDRATE)
+#define OUTPUT_STORE_WRITE_RETRIES 5 /* how many times to try Write16 & ResetDisk sequence */
+#define OUTPUT_STORE_EOF 26
+
+#define DIVIDE_UP(what,by) (((what)+(by)-1)/(by))
+#define ROUND_UP(what,by) (DIVIDE_UP((what),(by))*(by))
+
+#define ALLOC_KB DIVIDE_UP((ALLOC_END-START)+STACKSIZE_TOP,0x400)
+#define SECTORS_LEN ((SECTORS_END-START)/0x200)
+
+
+#define NIBBLE_TO_HEX1(nibble) ('0'+(nibble)+('A'-('9'+1))*((nibble&8)&&((nibble&2)||(nibble&4))))
+#define NIBBLE_TO_HEX(nibble) NIBBLE_TO_HEX1((nibble)&0xF)
+#define BYTE_TO_HEX(byte) NIBBLE_TO_HEX((byte)>>4),NIBBLE_TO_HEX((byte))
+
+/* Extract from : */
+#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
+#define UART_IER 1 /* Out: Interrupt Enable Register (DLAB=0) */
+#define UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */
+#define UART_LCR 3 /* Out: Line Control Register */
+#define UART_MCR 4 /* Out: Modem Control Register */
+#define UART_LSR 5 /* In: Line Status Register */
+#define UART_MSR 6 /* In: Modem Status Register */
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
+
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
+
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+
+
+/* Macros: */
+
+ .macro outchar char
+ movw $0x0E00 | \char,%ax
+ movw $7,%bx
+ int $0x10
+ .endm
+
+ .macro stosw_sniff
+#ifdef DISABLE_SNIFF
+ nop
+#else
+ stosw
+#endif
+ .endm
+
+#ifdef DISABLE_ORG
+#define MYORG(offset)
+#else
+#define MYORG(offset) .org (offset) + START /* "+ START" MUST be on the end of line - WHY?!? */
+#endif
+
+ .macro pushALL
+ pushw %ax
+ pushw %bx
+ pushw %cx
+ pushw %dx
+ pushw %si
+ pushw %di
+ pushw %bp
+ .endm
+
+ .macro popALL
+ popw %bp
+ popw %di
+ popw %si
+ popw %dx
+ popw %cx
+ popw %bx
+ popw %ax
+ .endm
+
+#define PUSHALL_SIZE (7*2)
+#define PUSHALL_SI ((1/*%bp*/+1/*%di*/)*2)
+
+ .macro CallJumpVector0x13
+ pushf /* create 'lret' stack by this pushf... */
+ pushw %cs /* ...this segment... */
+ call JumpVector0x13 /* ...and this return address */
+ .endm
+
+
+
+/* Code starts here */
+/********************/
+
+#ifdef HAVE_GAS_ARCH_I8086
+ .arch i8086,nojumps
+#endif
+ .code16
+ .text
+ .globl _start
+_start:
+
+/* init stack */
+START:
+ cli /* just a paranoia, shouldn't be needed */
+ jmp InitRawStack /* We don't want to have 0x90 on offset 2 (one of recognization rules by DOS) */
+ .org START+0x03
+
+ .macro places offset length string
+ .org (\offset) + START
+ .ascii "\string"
+ .org (\offset) + (\length) + START /* we can't check whether it isn't too short :-( */
+ .endm
+ .macro placex offset length bytes
+ .org (\offset) + START
+ .byte \bytes
+ .org (\offset) + (\length) + START /* we can't check whether it isn't too short :-( */
+ .endm
+
+/* We rather supply empty (=zeroed) FAT filesystem table
+ */
+ places 0x03,8,"I13Sniff" /* OEM ID */
+ placex 0x43,4,"0x5E,0x81,0xA2,0x99" /* Volume Serial Number: 5E81A299 as "SERIAL99" */
+ places 0x47,11,"Int13Sniff\0" /* Volume Label */
+ places 0x52,8,"BootOnly" /* Filesystem ID */
+
+InitRawStack:
+ .org 0x5A + START
+
+ xorw %ax,%ax
+ movw %ax,%ss
+ movw $0x7C00,%sp
+ sti
+ outchar '1'
+ ljmp $0x7C0/*segment*/ , $InitContinue7C0-START/*offset*/
+
+InitContinue7C0:
+ outchar '3'
+
+/* move to top memory */
+ xorw %dx,%dx /* DX=null */
+ movw %dx,%ds
+ movw %ds:MEM_KBYTES,%ax
+ subw $ALLOC_KB,%ax
+ movw %ax,%ds:MEM_KBYTES
+ movb $6,%cl
+ shlw %cl,%ax
+ movw %ax,%es
+ xorw %bx,%bx /* buffer=ES:BX= topram:0x0000 */
+ movw $0x0200 | SECTORS_LEN,%ax /* READ16 (SECTORS_LEN) sectors */
+ xorw %dx,%dx /* Head 0, Drive 0x00 */
+ call ReadSectors
+ xorw %di,%di
+ pushw %cs
+ popw %ds
+ xorw %si,%si
+ movw $0x200/2,%cx
+repe cmpsw
+ pushw %es
+ movw $InitContinueTop-START,%ax
+ pushw %ax
+ movw $BadCmpMsg-START,%si
+ jnz PrintFatal
+ cmpw $SECTOR2_MAGIC,%es:(Sector2_MagicBuf-START)
+ je LReturn1
+/* fallthru */
+PrintFatal:
+ call PrintString
+PrintFatal_dead:
+ jmp PrintFatal_dead
+LReturn1:
+ lret
+
+Return1:
+ ret
+
+/* Interface to retry-capable INT13, set everything except CX */
+/* function may destroy DS value! */
+ReadSectors:
+ movw $0x0001,%cx /* Cylinder 0, Sector 1 */
+ int $0x13
+ jnc Return1
+ pushw %dx /* save drive for resetting the controller */
+ pushw %ax /* error code in AH */
+ movw $InitMsg-START,%si
+ call PrintString
+ pushw %bx /* offset... */
+ pushw %es /* and segment of bufffer */
+ pushw %ax /* count */
+ pushw %dx /* head */
+ pushw %cx /* CX */
+ pushw %dx /* drive */
+ movw $SF13_02_Msg-START,%si
+ call PrintString
+ movw $SnifferErrTail_MSG-START,%si
+ call PrintString_noPref
+ movb $0x00,%ah /* reset controller - FDC or HDC */
+ popw %dx /* restore the drive */
+ int $0x13
+ jmp ReadSectors
+
+PrintTrailNL:
+ movw $TrailNL_Msg-START,%si
+ jmp PrintString_noPref
+
+/* String is given in SI, returns updated SI */
+PrintString:
+ movb $'%',%al
+ call PrintChar
+PrintString_noPref:
+ pushw %cs
+ popw %ds
+PrintString_loop:
+ cld
+ lodsb
+ testb %al,%al
+ jz Return1
+ movw $PrintString_loop-START,%bx /* from now do just 'ret' to close the loop */
+ pushw %bx
+ testb $0x80,%al /* should be (x&E0==E0) */
+ jz PrintChar
+ testb $0x10,%al
+ jz PrintString_no0x
+ pushw %ax
+ movb $'$',%al
+ call PrintChar
+ popw %ax
+PrintString_no0x:
+ popw %bx /* discard $PrintString_loop-START */
+ popw %bx /* return address */
+ popw %dx /* value to print */
+ pushw %bx /* return back the return address */
+ movw $PrintString_loop-START,%bx
+ pushw %bx
+ testb $0x02,%al
+ jz PrintString_noHhex
+ pushw %ax
+ pushw %dx
+ call PrintString_Lhex
+ popw %dx
+ popw %ax
+PrintString_noHhex:
+ movb %dl,%dh
+ testb $0x01,%al
+Return1_jump1:
+ jz Return1
+
+/* One byte to print is in DH */
+PrintString_Lhex:
+ pushw %dx
+ movb $4,%cl
+ rorb %cl,%dh
+ call PrintString_Nhex
+ popw %dx
+
+/* One nibble to print is in the lower one of DH */
+PrintString_Nhex:
+ andb $0x0F,%dh
+ addb $'0',%dh
+ cmpb $'9'+1,%dh
+ jc PrintString_charDH
+ addb $'A'-('9'+1),%dh
+PrintString_charDH:
+ movb %dh,%al
+/* fallthru */
+
+/* Character is given in AL
+ * Preserves: DS, ES, SI, DI
+ * Destroys: AX (even AL!), BX, CX, DX
+ * DS is sometimes left preserved, sometimes set to DS=CS
+ */
+PrintChar:
+#ifdef SCREEN_PRINT
+ movb $0x0E,%ah /* print character */
+ movw $7,%bx
+ pushw %ax
+ int $0x10
+ popw %ax
+#else
+ xorb %bh,%bh /* assumed below */
+#endif
+#if defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK)
+ pushw %cs
+ popw %ds /* set DS=CS */
+ cmpb %bh,%ds:(SerialEnable-START)
+ jz Return1_jump1
+#ifdef SERIAL_BAUDRATE
+ jmp SerialPrintChar
+#else /* SERIAL_BAUDRATE */
+ jmp OutputStorePrintChar
+#endif /* SERIAL_BAUDRATE */
+#else /* defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK) */
+ ret
+#endif /* defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK) */
+
+
+#if defined(SERIAL_BAUDRATE) || defined(OUTPUT_STORE_DISK)
+SerialEnable:
+ .byte 0 /* map of port bits which want to be enabled */
+
+#ifdef OUTPUT_STORE_DISK
+#define OUTPUT_STORE_SerialEnable (0x02) /* FloppyPrintChar enabled */
+#else
+#define OUTPUT_STORE_SerialEnable (0x00)
+#endif
+
+#endif
+
+InitMsg:
+ .asciz "INIT: "
+BadCmpMsg:
+ .asciz "Disk 0x00 sector 0 doesn't match myself!"
+SF13_02_Msg:
+ .ascii "Read"
+Common16disk_Msg:
+ .asciz "16(drv=\xF1,CX=\xF3,head=\xF2,count=\xF1,buf=\xF3:\xF3)"
+SnifferErrTail_MSG:
+ .ascii "=\xF2 !"
+TrailNL_Msg:
+ .byte 13,10,0
+
+/*******************************************************************/
+/* Storage area - code isn't needed to be primary-sector reachable */
+/*******************************************************************/
+
+/**************************************************************/
+/* Final primary-sector signature */
+/**************************************************************/
+
+ MYORG(0x1FC)
+ .byte 0xDE,0xAD /* FAT32 defines somehow extended, 32-bit signature (=> 0xDEAD) */
+ .byte 0x55,0xAA
+
+/**************************************************************/
+/* Section run during boot but from the upper half of code */
+/**************************************************************/
+
+
+/* Main initialization */
+/***********************/
+/* DS invalid, ES==CS */
+InitContinueTop:
+ outchar ':'
+
+ pushw %cs
+ cli
+ popw %ss
+ movw $ALLOC_KB*1024,%sp
+ sti
+
+#ifdef OUTPUT_STORE_DISK
+ movw $OutputStoreBufSpaceStart-START,%di
+ movw $((OutputStoreBuf+0x200-OutputStoreBufSpaceStart)+1)/2,%cx /* NOTE_1: Buffer may got +1 overfilled */
+ xorw %ax,%ax
+ rep stosw
+#endif
+
+#ifndef SERIAL_BAUDRATE
+#ifdef OUTPUT_STORE_DISK
+ movb $OUTPUT_STORE_SerialEnable,%cs:(SerialEnable-START)
+#endif
+ movw $HelloMsg-START,%si
+ call PrintString
+#else /* SERIAL_BAUDRATE */
+ movb $0x55|OUTPUT_STORE_SerialEnable,%es:(SerialEnable-START)
+ movw $HelloMsg-START,%si
+ call PrintString /* this string will not have EOL when SERIAL_BAUDRATE is defined */
+ movb %ds:(SerialEnable-START),%cl
+ movw $PORTADDR_BASE,%si
+SerialHello_loop:
+ xorw %ax,%ax
+ movw %ax,%ds
+ lodsw
+ rorb $1,%cl
+ jnc SerialHello_skipOne
+ testw %ax,%ax
+ jz SerialHello_skipOne
+ pushw %cx /* push SerialEnable mask, S=1 */
+ pushw %si /* push PORTADDR_BASE+x pointer, S=2 */
+ pushw %ax /* print port base */
+ movw $SerialHelloPort_Msg-START,%si
+ call PrintString_noPref
+ popw %si /* restore PORTADDR_BASE+x pointer, S=1 */
+ popw %cx /* restore SerialEnable mask, S=0 */
+SerialHello_skipOne:
+ rorb $1,%cl /* skip unused even bits of SerialEnable mask */
+ cmpw $PORTADDR_BASE+4*2,%si
+ jne SerialHello_loop
+ call PrintTrailNL
+#endif
+
+/* now install our SniffFunction0x13 sniffer */
+
+ xorw %ax,%ax
+ movw %ax,%ds
+ movw $0x13*4,%si
+ pushw %cs
+ popw %es
+ movw $OrigVector0x13-START,%di
+ pushw %si
+ movw $SniffFunction0x13-START,%ax
+ cld
+ cli
+ movsw
+ movsw
+ popw %di /* = $0x13*4 */
+ pushw %ds
+ popw %es /* = null */
+ stosw_sniff /* $SniffFunction0x13-START */
+ pushw %cs
+ popw %ax
+ stosw_sniff
+ movw $0x18*4,%di /* ROM basic - failed boot */
+ movw $SniffFunction0x18-START,%ax
+ stosw_sniff
+ pushw %cs
+ popw %ax
+ stosw_sniff
+ sti
+#ifdef OUTPUT_STORE_DISK
+ incb %cs:(OutputStoreFlushEnable-START) /* we have now functional JumpVector0x13 */
+#endif
+ /* we WANT DS left with 0x0000 */
+ /* we WANT ES left with 0x0000 */
+
+/* and give the system control to disk=0x80/masterboot */
+ movw $0x0201,%ax /* READ16 1 sector */
+ movw $0x7C00,%bx /* buffer=ES:BX= 0x0000:0x7C00 */
+ pushw %ds
+ pushw %bx /* BX/DS on stack for later 'lret', S=2, prepared for far ret */
+ movw $0x0080,%dx /* Head 0, Drive 0x80 */
+ call ReadSectors /* it returns only when successfully read */
+ cmpw $0xAA55,%es:(0x7C00+0x1FE) /* 0x55,0xAA */
+ movw $BadSignatureMsg-START,%si
+ jne PrintFatal_jump1
+ cli /* IMPORTANT: Boot sectors must be run with CLI! */
+ lret /* lret to 0x0000:0x7C00 */
+
+SniffFunction0x18:
+ movw $Interrupt0x18Msg-START,%si /* %ds gets fixed in PrintFatal */
+PrintFatal_jump1:
+ jmp PrintFatal
+
+
+/* SerialPort handling code */
+/****************************/
+
+#ifdef SERIAL_BAUDRATE
+
+SerialPrintChar:
+
+/* Function assumes DS == CS, it preserves SI, DI, ES but uses ES==0 ! */
+/* Function expects character to print in AL, preserves it. */
+
+/* fallthru */
+SerialLoop:
+ movb $0,%ah /* offset from PORTADDR_BASE for port #1 */
+ xorw %bx,%bx /* any register */
+ pushw %es /* push ES, S=1 */
+ movw %bx,%es
+
+SerialLoop_loop:
+ movw $PORTADDR_BASE,%bx
+ addb %ah,%bl
+ movw %es:(%bx),%bx
+ testw %bx,%bx
+ jz SerialLoop_noport /* now we have port base in BX */
+ movb $1,%ch
+ movb %ah,%cl
+ rolb %cl,%ch
+ testb %ch,%ds:(SerialEnable-START)
+ jz SerialLoop_noport /* disabled port */
+ pushw %ax /* push CHARACTER and AH(port offset), S=2 */
+ cmpb $'%',%al
+ jnz SerialLoop_noInit
+
+/* Serial Init */
+ leaw UART_IER(%bx),%dx /* DX=+1, UAT_IER - interrupt enable */
+ xorb %al,%al /* NO interrupts */
+ outb %al,%dx
+ incw %dx
+ incw %dx /* DX=+3, UART_LCR - used as divisor enable */
+ movb $SERIAL_LCR_INIT | UART_LCR_DLAB,%al
+ outb %al,%dx
+ movw %bx,%dx /* DX=+0, UART_DLL - divisor LOW */
+ movb $(SERIAL_DIVISOR & 0xFF),%al
+ outb %al,%dx
+ incw %dx /* DX=+1, UART_DLM - divisor HIGH */
+ movb $(SERIAL_DIVISOR >> 8),%al
+ outb %al,%dx
+ incw %dx
+ incw %dx /* DX=+3, UART_LCR - line control */
+ movb $SERIAL_LCR_INIT,%al
+ outb %al,%dx
+ incw %dx /* DX=+4, UART_MCR - modem control */
+ movb $SERIAL_MCR_INIT,%al
+ outb %al,%dx
+ incw %dx /* DX=+5, UART_LSR - line status */
+ inb %dx,%al
+ incb %al
+ jz PrintChar_serial_invalidPort /* LSR port is 0xFF - no device on the bus */
+
+#ifdef SERIAL_REQUIRE_DSR
+ incw %dx /* DX=+6, UART_MSR - modem status */
+ xorw %cx,%cx /* Try ~ 60msec for DSR transition delayd by pure wiring after our DTR */
+PrintChar_serialWaitDSR:
+ inb %dx,%al
+ andb $UART_MSR_DSR,%al /* we MUST have AL==0 when passing to invalidPort ! */
+ jnz PrintChar_serialSend
+ loop PrintChar_serialWaitDSR
+ jmp PrintChar_serial_invalidPort
+#endif /* SERIAL_REQUIRE_DSR */
+
+SerialLoop_loop_jump1:
+ jmp SerialLoop_loop
+
+SerialLoop_noInit: /* still CHARACTER and AH(port offset) on stack, S=2 */
+PrintChar_serialSend:
+ movb $SERIAL_SENDWAIT,%ah
+PrintChar_serialSend_wait:
+ leaw UART_LSR(%bx),%dx /* UART_LSR - line status */
+ inb %dx,%al
+ andb $UART_LSR_THRE,%al /* test transmitter-buffer-empty bit */
+ jnz PrintChar_serialSend_free /* we MUST have AL==0 when passing to invalidPort ! */
+ loop PrintChar_serialSend_wait
+ decb %ah /* out-loop count SERIAL_SENDWAIT */
+ jnz PrintChar_serialSend_wait
+PrintChar_serial_invalidPort:
+ leaw UART_MCR(%bx),%dx /* UART_LSR - line status */
+ outb %al,%dx /* assumed AL==0: turn off DTR & RTS of the failing port */
+ movb %ds:(SerialEnable-START),%ah
+ pushw %ax /* pushed AH(SerialEnable), S=3 */
+ movb %al,%ds:(SerialEnable-START) /* assumed AL==0; disable temporarily complete serial output */
+ pushw %si /* save SI, S=4 */
+ pushw %bx /* port base to print out */
+ movw $SerialPortError_Msg-START,%si
+ call PrintString
+ popw %si /* recover SI, S=3 */
+ popw %bx /* restored BH=AH(SerialEnable), S=2 */
+ popw %ax /* restored CHARACTER and AH(port offset), S=1 */
+ movb %ah,%cl
+ movb $0xFE /* ==~1 */,%ch
+ rolb %cl,%ch /* masking-out pattern */
+ andb %ch,%bh /* mask-out the original SerialEnable left in BH */
+ movb %bh,%ds:(SerialEnable-START) /* disable the failed port for futher serial dumping */
+ jmp SerialLoop_noport
+
+PrintChar_serialSend_free:
+ movw %bx,%dx /* UART_TX - xmit buffer */
+ popw %ax /* restored CHARACTER and AH(port offset), S=1 */
+ outb %al,%dx /* final character transmit */
+
+SerialLoop_noport:
+ inc %ah
+ inc %ah /* skip by two bytes of PORTADDR_BASE entry offset */
+ cmp $4*2,%ah
+ jne SerialLoop_loop_jump1
+ popw %es /* pop ES, S=0 */
+#ifndef OUTPUT_STORE_DISK
+ ret
+#else
+/* FALLTHRU */
+#endif
+#endif /* SERIAL_BAUDRATE */
+
+/* OutputStore handling code */
+/*****************************/
+
+#ifdef OUTPUT_STORE_DISK
+
+#if OUTPUT_STORE_DISK!=0x00
+#error "int13sniff WILL destroy the data on OUTPUT_STORE_DISK!!! Are you sure?"
+#endif
+
+OutputStorePrintChar:
+
+/* Function assumes DS == CS, it preserves SI, DI, ES. */
+/* Function expects character to print in AL. */
+
+ testb $OUTPUT_STORE_SerialEnable,%ds:(SerialEnable-START)
+ jz Return3
+OS_NotDisabled:
+ cmp $13,%al /* we completely ignore and filter-out all '\r' */
+ je Return3
+ pushw %es /* SAVE %ES */
+ pushw %di /* SAVE %DI */
+ pushw %si /* SAVE %SI */
+ movw %ds:(OutputStoreBufPtr-START),%di
+ pushw %cs
+ popw %es /* ES=CS */
+ stosb
+OutputStoreTerminate: /* we will terminate the OutputStoreBuf at the given %DI position */
+ movw %di,%ds:(OutputStoreBufPtr-START)
+ movb $OUTPUT_STORE_EOF,%ds:(%di)
+ cmpb $0,%ds:(OutputStoreFlushEnable-START)
+ jz Return2pop_jump1 /* we cannot yet call CallJumpVector0x13 */
+ cmpb $10,%al
+ je OutputStoreFlush /* always flush bufer on message newline */
+ cmpw $(OutputStoreBuf-START)+0x200,%di
+ jnc OutputStoreFlush
+Return2pop_jump1:
+ jmp Return2pop
+
+Return3:
+ ret
+
+OutputStoreFlush:
+ movw $OUTPUT_STORE_WRITE_RETRIES,%bp
+OS_RetryWrite:
+ mov $0x08,%ah /* %AH=get drive geometry, WARNING: DESTROYS %DI! */
+ mov $OUTPUT_STORE_DISK,%dl
+ CallJumpVector0x13 /* now %DH=maximum heads, %CX=maximum cylinder§or */
+ jc OS_MediaOver
+ movw %cx,%bx
+ xchg %bh,%bl
+ rol $1,%bh
+ rol $1,%bh
+ andb $0x03,%bh /* now %BX==# of cylinders */
+ andb $0x3F,%cl /* now %CL==# of sectors */
+ pushw %cx /* SAVE LOW==# of sectors */
+ inc %dh /* now %DH==# of heads */
+ movb %dh,%al
+ mulb %cl /* now %AX==# of sectors in cylinder */
+ movw %ax,%cx
+ /* now we have %CX==# of sectors in cylinder, %BX=# of cylinders */
+ movw %cs:(OutputStoreAddress-START+0),%ax
+ movw %cs:(OutputStoreAddress-START+2),%dx /* %DX:%AX==OutputStoreAddress */
+ divw %cx /* now %AX==cylinder, %DX==head§or */
+ cmp %ax,%bx
+ popw %bx /* RESTORE ->%BL==# of sectors */
+ jc OS_MediaOver /* cylinders exceeded */
+ xchg %ax,%dx
+ divb %bl /* now %AL==head, %AH=0-based sector (and %DX==cylinder) */
+ incb %ah /* now %AL==head, %AH=1-based sector (and %DX==cylinder) */
+ testb $0xC0,%ah
+ jnz OS_MediaOver
+ testb $0xFC,%dh
+ jnz OS_MediaOver
+ movb %ah,%cl
+ movb %dl,%ch
+ rorb $1,%dh
+ rorb $1,%dh
+ orb %dh,%cl /* now %CX==cylinder§or in BIOS format */
+ movb %al,%dh /* now %DH==head */
+ pushw %cs
+ popw %ds /* DS=CS */
+ movb $OUTPUT_STORE_DISK,%dl
+ movw $0x0301,%ax /* %AH=write sectors, %AL=1 sector */
+ movw $(OutputStoreBuf-START),%bx
+ pushw %cs
+ popw %es /* ES=CS */
+ CallJumpVector0x13
+ jnc OS_WriteOK
+ movw %ax,%es /* %ES=Write16 error code */
+ xorb %ah,%ah /* %AH=reset drive */
+ CallJumpVector0x13
+ decw %bp
+ jnz OS_RetryWrite
+ andb $~OUTPUT_STORE_SerialEnable,%ds:(SerialEnable-START) /* OutputStorePrintChar got DISABLED here */
+ pushw %es /* Write16 error code, for secondary "SnifferErrTail_MSG" */
+ /* now parameters for "Common16disk_Msg": */
+ pushw %bx /* buf 2nd */
+ pushw %cs /* buf 1st (%ES has been destroyed by "Write16 error code" */
+ movb $0x01,%al /* "count" has been destroyed by "reset disk" call */
+ pushw %ax /* count */
+ pushw %dx /* head */
+ pushw %cx /* CX */
+ pushw %dx /* drive */
+
+OS_MediaOver:
+ movw $OutputFailedWrite_Msg-START,%si
+ call PrintString
+ movw $Common16disk_Msg-START,%si
+ call PrintString_noPref /* pops up 6*2 bytes (6 words) */
+ movw $SnifferErrTail_MSG-START,%si
+ call PrintString_noPref /* pops up 1*2 bytes (1 word ) */
+Return2pop:
+ popw %si /* RESTORE %SI */
+ popw %di /* RESTORE %DI */
+ popw %es /* RESTORE %ES */
+Return2: ret
+
+OS_WriteOK:
+ cmpw $(OutputStoreBuf-START)+0x200,%ds:(OutputStoreBufPtr-START)
+ jc Return2pop /* buffer is not yet filled */
+ incw %ds:(OutputStoreAddress-START+0)
+ jnz OS_IncAddrNotWordCarry
+ incw %ds:(OutputStoreAddress-START+2)
+OS_IncAddrNotWordCarry:
+ movw $OutputStoreBuf-START,%di
+ pushw %di
+ xorw %ax,%ax
+ movw $0x200/2,%cx
+ rep stosw /* OutputStore got cleared */
+ popw %di
+ movb $10,%al /* we want to immediately Flush even the new sector */
+ jmp OutputStoreTerminate
+
+#endif /* OUTPUT_STORE_DISK */
+
+/**************************************************************/
+/* Section for INT13 sniffing */
+/* THIS point may start to be after 0x200 boundary */
+/**************************************************************/
+
+SniffFunction0x13:
+ pushw %ax /* trash */
+ pushw %ds
+ pushALL
+
+ cmpb $0x00,%ah /* reset */
+ jne SF13_not00
+ movw $JumpVector0x13popa-START,%ax
+ pushw %ax /* return address */
+ pushw %dx /* drive */
+ movw $SF13_00_Msg-START,%si
+JumpVector0x13printStringPopa:
+ call PrintString
+ jmp PrintTrailNL
+
+SF13_not00:
+ cmpb $0x02,%ah /* read16 */
+ jne SF13_not02
+ pushw %bx /* buf 2nd */
+ pushw %es /* buf 1st */
+ pushw %ax /* count */
+ pushw %dx /* head */
+ pushw %cx /* CX */
+ pushw %dx /* drive */
+ movw $SF13_02_Msg-START,%si
+ call PrintString
+Sniffer13bottomStdHalf:
+ call CallVector0x13
+ movw $SnifferOKTail_Msg-START,%si
+ jnc SF13_not00_tail /* ...if CY is SET */
+Sniffer13DumpErrorAH:
+ movw $SnifferErrTail_MSG-START,%si
+ pushw %ax
+SF13_not00_tail:
+Sniffer13iretPrintStringFinal:
+ call PrintString_noPref
+ popALL
+ popw %ds
+ incw %sp
+ incw %sp /* trash discarded */
+ iret
+
+SF13_not02:
+ cmpb $0x41,%ah /* presence32 */
+ jne SF13_not41
+ cmpw $0x55AA,%bx
+ jne SF13_not41
+ pushw %dx /* drive */
+ movw $SF13_41_Msg-START,%si
+ call PrintString
+ call CallVector0x13
+ cmp $0xAA55,%bx
+ jne Sniffer13DumpErrorAH
+ pushw %cx /* supports */
+ pushw %ax /* version */
+ movw $SF13_41_out_Msg-START,%si
+ jmp Sniffer13iretPrintStringFinal
+
+CallVector0x13:
+ popw %ax /* return address */
+ pushw %bp
+ movw %sp,%bp
+ movw %ax,%ss:2/*%bp*/+PUSHALL_SIZE+2/*%ds*/(%bp) /* store %ax to 'trash' */
+ popw %bp
+ popALL
+ popw %ds
+ CallJumpVector0x13
+ pushw %ds
+ pushALL /* stack is back to normal NOW */
+ pushw %bp /* trash - just to prepare return address16 */
+ pushw %ax
+ pushw %bp
+ movw %sp,%bp
+ movw %ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/(%bp),%ax
+ movw %ax,%ss:2/*%bp*/+2/*ax*/(%bp)
+ pushf
+ pop %ax
+ movw %ax,%ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/+2/*trash*/+4/*ret-seg:offs*/(%bp)
+ popw %bp
+ popw %ax
+ ret /* to prepared return16 and now will be stack in normal again */
+
+SF13_not41:
+ cmpb $0x42,%ah /* read32 */
+ je SF13_do42
+ jmp JumpVector0x13popa /* too far for conditional-jump */
+
+SF13_do42:
+ movb %ds:2(%si),%al /* count */
+ pushw %ax
+ pushw %ds:8(%si) /* LBA32 sector LOWEST */
+ pushw %ds:8+2(%si) /* LBA32 sector LOWER */
+ pushw %ds:8+4(%si) /* LBA32 sector HIGHER */
+ pushw %ds:8+6(%si) /* LBA32 sector HIGHEST */
+ pushw %dx /* drive */
+ movw $SF13_42_Msg-START,%si
+ call PrintString /* first half of the entry-message */
+
+ movw %sp,%bp
+ movw %ss:PUSHALL_SI(%bp),%si
+ movw %ss:PUSHALL_SIZE(%bp),%ds /* restore DS:SI packet */
+
+ movw %ds:4(%si),%ax /* buf16 offset */
+ pushw %ax /* buf16 offset */
+ andw %ds:4+2(%si),%ax /* buf16 segment */
+ pushw %ds:4+2(%si) /* buf16 segment */
+ incw %ax
+ movw $SF13_42_buf16_Msg-START,%si
+ jnz SF13_42_buf16 /* buf16 was NOT 0xFFFF:0xFFFF => jump as OK */
+ addw $4,%sp /* drop buf16 address seg:offs */
+ pushw %ds:0x10(%si) /* 64-bit buffer LOWEST */
+ pushw %ds:0x10+2(%si) /* 64-bit buffer LOWER */
+ pushw %ds:0x10+4(%si) /* 64-bit buffer HIGHER */
+ pushw %ds:0x10+6(%si) /* 64-bit buffer HIGHEST */
+ movw $SF13_42_buf64_Msg-START,%si
+
+SF13_42_buf16: /* too far for conditional-jump */
+ call PrintString_noPref
+ jmp Sniffer13bottomStdHalf
+
+JumpVector0x13popa:
+ popALL
+ popw %ds
+ incw %sp
+ incw %sp /* trash discarded */
+JumpVector0x13:
+ .byte 0xEA /* ljmp */
+OrigVector0x13:
+ .skip 4
+
+/* Serial support upper half initialization messages */
+/*****************************************************/
+
+#ifdef SERIAL_BAUDRATE
+
+SerialPortError_Msg:
+ .asciz "SerialPort \xF3 error: stopping its communication\r\n"
+
+SerialHelloPort_Msg:
+ .asciz " \xF3"
+
+#endif /* SERIAL_BAUDRATE */
+
+
+/* Main upper half initialization messages */
+/*******************************************/
+
+HelloMsg:
+ .ascii "INT13-Sniff, version ",VERSION,", RCS revision ",REVISION
+#ifdef OUTPUT_STORE_DISK
+ .ascii ", Output Store on disk 0x"
+ .byte BYTE_TO_HEX(OUTPUT_STORE_DISK)
+#endif
+#ifdef SERIAL_BAUDRATE
+ .ascii ", using serial ports:"
+#else
+ .byte 13,10
+#endif
+ .byte 0
+BadSignatureMsg:
+ .asciz "Disk 0x80 has invalid signature!"
+
+
+/* Interrupt sniffing messages */
+/*******************************/
+
+Interrupt0x18Msg:
+ .asciz "Interrupt 0x18 - Failed boot!"
+SF13_00_Msg:
+ .asciz "Reset(drv=\xF1)"
+SF13_41_Msg:
+ .asciz "Presence32(drv=\xF1)"
+SF13_41_out_Msg:
+ .ascii "=(version=\xF2,supports=\xF3)"
+ /* fallthru */
+SnifferOKTail_Msg:
+ .ascii " OK"
+ .byte 13,10,0
+SF13_42_Msg:
+ .asciz "Read32(drv=\xF1,LBA32=\xF3\xE3\xE3\xE3,count=\xF1,buf="
+SF13_42_buf16_Msg:
+ .asciz "\xF3:\xF3)"
+SF13_42_buf64_Msg:
+ .asciz "\xF3\xE3\xE3\xE3)"
+
+
+/* Output Store messages / variables */
+/*************************************/
+#ifdef OUTPUT_STORE_DISK
+
+OutputFailedWrite_Msg:
+ .asciz "Output Store error, stopping store: Write"
+
+OutputStoreBufPtr:
+ .word OutputStoreBufSpaceStart-START
+OutputStoreAddress:
+#ifdef OUTPUT_STORE_ADDRESS
+ .long OUTPUT_STORE_ADDRESS
+#else
+ .long (OutputStoreBuf-START)/0x200
+#endif
+OutputStoreFlushEnable:
+ .byte 0 /* disable calling of JumpVector0x13 as it is not functional yet */
+
+#endif
+/**************************************************************/
+/* Sectors finalisation */
+/**************************************************************/
+
+#ifndef OUTPUT_STORE_DISK
+ALLOC_END:
+#endif
+
+ MYORG(ROUND_UP(.+2-START,0x200)-2)
+Sector2_MagicBuf:
+ .word SECTOR2_MAGIC
+
+#ifdef OUTPUT_STORE_DISK
+OutputStoreBuf:
+#endif
+ .ascii "\n\n"
+#ifdef OUTPUT_STORE_DISK
+OutputStoreBufSpaceStart:
+ .ascii "Buffer empty, no data stored yet.\n"
+#else
+ .ascii "OUTPUT_STORE_DISK not defined, Output Store disabled!\n"
+#endif
+ .byte OUTPUT_STORE_EOF
+
+ MYORG(ROUND_UP(.-START,0x200)) /* WARNING, we may overfull the buffer by +1 bytes, see NOTE_1 */
+
+#ifdef OUTPUT_STORE_DISK
+ .equ ALLOC_END,.+1 /* we need one 'trash' byte for exceeding OUTPUT_STORE_EOF */
+#endif
+
+SECTORS_END:
+
+/* vi:ts=8:sw=8
+ */
diff --git a/project/line9k/Index.pm b/project/line9k/Index.pm
index ab2186c..7d2e143 100644
--- a/project/line9k/Index.pm
+++ b/project/line9k/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>420,
"icon"=>"charger-icon.jpeg",
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/line9k',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/line9k',
"summary"=>"Modem / Nokia 9110 GSM data gateway control, charger",
"license"=>"PD",
"maintenance"=>"ready",
diff --git a/project/lynxilla/Index.pm b/project/lynxilla/Index.pm
index 7edab7f..1a43a85 100644
--- a/project/lynxilla/Index.pm
+++ b/project/lynxilla/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"web",
"trivia"=>1,
"priority"=>460,
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=.userContent.css',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=.userContent.css',
"summary"=>sub {
return a_href('http://lynx.isc.org/','Lynx').' look&feel with '
.a_href('http://www.mozilla.org/','Mozilla').' web compatibility';
diff --git a/project/netdnsspoof/Index.pm b/project/netdnsspoof/Index.pm
index 45eed69..707093c 100644
--- a/project/netdnsspoof/Index.pm
+++ b/project/netdnsspoof/Index.pm
@@ -33,7 +33,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>483,
"cvs"=>"netdnsspoof",
- "link-Source file"=>'http://git.jankratochvil.net/?p=netdnsspoof.git;a=blob_plain;hb=HEAD;f=netdnsspoof',
+ "link-Source file"=>'//git.jankratochvil.net/?p=netdnsspoof.git;a=blob_plain;hb=HEAD;f=netdnsspoof',
"summary"=>"Network DNS proxy spoofing any nonexisting names",
"license"=>"GPL",
"maintenance"=>"ready",
diff --git a/project/postget/Index.pm b/project/postget/Index.pm
index 34794e1..c205e4f 100644
--- a/project/postget/Index.pm
+++ b/project/postget/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"web",
"trivia"=>1,
"priority"=>380,
-# "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=WWW/cgi-bin/postget.php',
+# "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=WWW/cgi-bin/postget.php',
"download"=>"postget.php",
"summary"=>sub {
return "Bookmark ".a_href('http://www.w3.org/TR/html4/interact/forms.html#h-17.13.1','POST')
diff --git a/project/ppp9k/Index.pm b/project/ppp9k/Index.pm
index 9cf5c6b..ed37537 100644
--- a/project/ppp9k/Index.pm
+++ b/project/ppp9k/Index.pm
@@ -31,7 +31,7 @@ our @ListItem=(
"name"=>"ppp9k",
"platform"=>"unixuser",
"priority"=>410,
- "download"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/ppp9k',
+ "download"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/ppp9k',
"summary"=>sub {
return "Connect "
.a_href('http://www.nokia.com/phones/9110i','Nokia Communicator')
diff --git a/project/redirector_ad/Index.pm b/project/redirector_ad/Index.pm
index bfa906e..3a9fc59 100644
--- a/project/redirector_ad/Index.pm
+++ b/project/redirector_ad/Index.pm
@@ -34,7 +34,7 @@ our @ListItem=(
"icon"=>"cnet-icon.png",
"cvs"=>"redirector-ad",
"summary"=>sub { return 'Banner killer as '.a_href('http://www.squid-cache.org/','Squid').' redirector filter'; },
- "link-README"=>'http://git.jankratochvil.net/?p=redirector-ad.git;a=blob_plain;hb=HEAD;f=README',
+ "link-README"=>'//git.jankratochvil.net/?p=redirector-ad.git;a=blob_plain;hb=HEAD;f=README',
"license"=>"PD",
"maintenance"=>"ready",
"language"=>"Perl",
diff --git a/project/staticbuild/Index.pm b/project/staticbuild/Index.pm
index 251d8b9..0dce3ef 100644
--- a/project/staticbuild/Index.pm
+++ b/project/staticbuild/Index.pm
@@ -68,7 +68,7 @@ I did not need it anywhere and it gets everything simpler and smaller this way.<
although without gcc -static. As the last step you should relink the binaries against
this staticbuild libraries by hand - it is not much possible to convince
automake(1) to properly relink it the minized way for you. Check the
-'@{[ a_href 'http://git.jankratochvil.net/?p=captive.git;a=blob_plain;hb=HEAD;f=build-static','build-static' ]}'
+'@{[ a_href '//git.jankratochvil.net/?p=captive.git;a=blob_plain;hb=HEAD;f=build-static','build-static' ]}'
script for an example.
This package has been used in its current or former versions for my projects:
HERE
diff --git a/project/tac_plus/Index.pm b/project/tac_plus/Index.pm
index c1f69e2..ca1de5f 100644
--- a/project/tac_plus/Index.pm
+++ b/project/tac_plus/Index.pm
@@ -32,7 +32,7 @@ our @ListItem=(
"platform"=>"unixuser",
"priority"=>270,
"icon"=>"cisco-icon.jpeg",
- "download-GTS rel.4 diff for TACACS+ v4.0.3"=>"tac_plus-F4.0.3.alpha.8.gts4.diff.gz",
+ "download-GTS rel.4 diff for TACACS+ v4.0.3"=>"tac_plus-F4.0.3.alpha.8.gts4.diff",
"download-TACACS+ v4.0.3, Devrim Seral rel.8"=>"http://www.gazi.edu.tr/tacacs/index.php?page=download",
"cvs"=>"tac_plus",
"link-Freshmeat"=>sub {
@@ -65,11 +65,11 @@ include:
multiple "member
" keyword memberships supported
"host
" entity unified with "user
"/"group
" entities
"when
" blocks implemented for NAS host based configuration
-all Makefile.in
options moved to
- configure.in
-Makefile.in
rewritten to
+all Makefile.in
options moved to
+ configure.in
+Makefile.in
rewritten to
automake
- Makefile.am
+ Makefile.am
HERE
diff --git a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff b/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff
new file mode 100644
index 0000000..19303bc
--- /dev/null
+++ b/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff
@@ -0,0 +1,15697 @@
+diff --git a/.exrc b/.exrc
+new file mode 100644
+index 0000000..d51ef7d
+--- /dev/null
++++ b/.exrc
+@@ -0,0 +1,2 @@
++set tabstop=8
++set shiftwidth=4
+diff --git a/Makefile.am b/Makefile.am
+new file mode 100644
+index 0000000..8d609b1
+--- /dev/null
++++ b/Makefile.am
+@@ -0,0 +1,126 @@
++# Please NOTE: None of the TACACS code available here comes with any
++# warranty or support.
++# Copyright (c) 1995-1998 by Cisco systems, Inc.
++#
++# Permission to use, copy, modify, and distribute this software for any
++# purpose and without fee is hereby granted, provided that this
++# copyright and permission notice appear on all copies of the software and
++# supporting documentation, the name of Cisco Systems, Inc. not be used
++# in advertising or publicity pertaining to distribution of the
++# program without specific prior permission, and notice be given
++# in supporting documentation that modification, copying and distribution is by
++# permission of Cisco Systems, Inc.
++#
++# Cisco Systems, Inc. makes no representations about the suitability of this
++# software for any purpose. THIS SOFTWARE IS PROVIDED ``AS IS''
++# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
++# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++# FOR A PARTICULAR PURPOSE.
++
++
++AUTOMAKE_OPTIONS = foreign
++
++sbin_PROGRAMS = tac_plus
++bin_PROGRAMS = generate_passwd
++man_MANS = tac_plus.1
++noinst_MANS = tac_regexp.3
++tacacssysconfdir = $(sysconfdir)/tacacs
++tacacssysconf_DATA = tac_plus.cfg
++EXTRA_DIST = \
++ CHANGES \
++ README.LDAP \
++ README.PAM \
++ users_guide \
++ tac_plus.spec.in \
++ convert.pl \
++ tac_plus.cfg \
++ tac_plus.init \
++ tac_plus.rotate \
++ tac_plus.sql \
++ tac_plus.pam \
++ $(tacacssysconf_DATA) \
++ $(man_MANS) \
++ $(noinst_MANS)
++
++generate_passwd_SOURCES = generate_passwd.c
++
++tac_plus_SOURCES = \
++ acct.c acct.h \
++ authen.c authen.h \
++ author.c author.h \
++ cfgfile.c cfgfile.h \
++ cfgeval.c cfgeval.h \
++ choose_authen.c choose_authen.h \
++ default_fn.c default_fn.h \
++ default_v0_fn.c default_v0_fn.h \
++ do_acct.c do_acct.h \
++ do_author.c do_author.h \
++ dump.c dump.h \
++ enable.c enable.h \
++ encrypt.c encrypt.h \
++ expire.c expire.h \
++ hash.c hash.h \
++ main.c main.h \
++ md5.c md5.h \
++ packet.c packet.h \
++ parse.c parse.h \
++ programs.c programs.h \
++ pw.c pw.h \
++ pwlib.c pwlib.h \
++ report.c report.h \
++ sendauth.c sendauth.h \
++ sendpass.c sendpass.h \
++ time_limit.c time_limit.h \
++ utils.c utils.h \
++ mschap.h \
++ tac_regmagic.h \
++ tac_plus.h
++
++EXTRA_tac_plus_SOURCES = $(cond)
++tac_plus_LDFLAGS = $(conf_LDFLAGS)
++# $(use_o) has to be BEFORE $(conf_LDADD)! (for library dependencies)
++tac_plus_LDADD = $(use_o) $(conf_LDADD)
++tac_plus_DEPENDENCIES = $(use_o)
++use = @COND_USE@
++use_o = $(filter %.o,$(use:.c=.o))
++
++cond_DB = db.c db.h
++cond_DB_MYSQL = db_mysql.c db_mysql.h
++cond_DB_NULL = db_null.c db_null.h
++cond_DB_PGSQL = db_pgsql.c db_pgsql.h
++cond_USE_LDAP = ldap_author.c ldap_author.h
++cond_MAXSESS = maxsess.c maxsess.h
++cond_MSCHAP = md4.c md4.h
++cond_SKEY = skey_fn.c skey_fn.h
++cond_USE_PAM = tac_pam.c tac_pam.h
++cond_TCPWRAPPER = tcpwrap.c tcpwrap.h
++cond_WITH_INCLUDED_REGEX = \
++ tac_regexp.c tac_regexp.h
++
++cond = \
++ $(cond_DB) \
++ $(cond_DB_MYSQL) \
++ $(cond_DB_NULL) \
++ $(cond_DB_PGSQL) \
++ $(cond_USE_LDAP) \
++ $(cond_MAXSESS) \
++ $(cond_MSCHAP) \
++ $(cond_SKEY) \
++ $(cond_USE_PAM) \
++ $(cond_TCPWRAPPER) \
++ $(cond_WITH_INCLUDED_REGEX)
++
++
++# These rules were not migrated from Makefile.in as I don't have
++# (and I don't know) 'purify' tool:
++#
++# purecov: $(OBJS) $(LIBS)
++# purecov -follow-child-processes -handle-signals=SIGTERM \
++# -append-logfile -log-file=purecov.log \
++# -cache-dir=`pwd` \
++# $(CC) -o tac_plus $(CFLAGS) $(OBJS) $(LIBS) $(OSLIBS)
++#
++# purify: $(OBJS) $(LIBS)
++# purify -follow-child-processes=yes -log-file=./tac_plus_purify.log \
++# -handle-signals=SIGTERM -cache-dir=. \
++# $(CC) -o tac_plus $(CFLAGS) $(OBJS) $(LIBS) $(OSLIBS)
+diff --git a/README.LDAP b/README.LDAP
+new file mode 100644
+index 0000000..755b19e
+--- /dev/null
++++ b/README.LDAP
+@@ -0,0 +1,146 @@
++ LDAP Authentification with Tacacs+
++ ----------------------------------
++
++
++Author : Harpes Patrick (patrick.harpes@tudor.lu)
++ Jahnen Andreas (andreas.jahnen@tudor.lu)
++Date : 16.03.2001
++
++License:
++--------
++
++ tac_ldap 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 2,
++ or (at your option) any later version.
++
++
++This document aim to describe how to perform LDAP authentification for tacacs+.
++
++
++Requirements:
++-------------
++
++1) tac_plus.F4.0.3-v8.alpha.tar.gz
++ This package includes the original CISCO tacacs+ package from http://www.gazi.edu.tr/tacacs/
++2) openldap package
++ This package has been developped using the openldap libraries version 2.0.7
++ OpenLDAP is available from www.openldap.org
++3) GCC and other GNU developpment tools (make...)
++4) A running LDAP server (test has been made using Lotus Domino LDAP server version 5.0.x and
++ OpenLDAP)
++
++Overview:
++---------
++ ------------ ----------------
++ - Server - - Notes DOMINO -
++ ---------------- - running -____LDAP____- LDAP Server -
++ - CISCO Dialup -__tacacs+_____- tacacs+ - - or -
++ - Router - - - - other LDAP -
++ ---------------- ------------ - Server -
++ ---------------
++
++The CISCO router sends tacacs+ request to the tacacs+ server. This one uses the LDAP
++server to authentificate the user.
++
++
++HowTo configure the CISCO router?
++---------------------------------
++
++There are good documentations available on how to set up the CISCO router for using
++tacacs+. This documents can be found on the tacacs+ homepage http://www.gazi.edu.tr/tacacs/
++
++HowTo install the tacacs+ package with LDAP support?
++----------------------------------------------------
++
++To enable the LDAP support on the tacacs+ package, you have to perform the following steps:
++
++ 1. Install the Open LDAP package (version 2.0.7) (www.openldap.org)
++ Refer to the INSTALL document to build this package.
++
++ 2. Unpack the tacacs+ package in /usr/local/src
++ # tar -zxvf tac_plus.F4.0.3-v8.alpha.tar.gz
++
++ 3. Use the configure script to create the Makefiles
++
++ # cd /usr/local/src/tac_plus.F5.0.0.alpha/
++ # ./configure --with-ldap
++
++ You can use ./configure --help to get more options
++
++ 4. Compile the package
++
++ # make tac_plus
++
++ 5. Set your LD_LIBRARY_PATH to include the LDAP libraries
++
++ # LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH
++
++
++HowTo configure tacacs+ for using the LDAP support
++--------------------------------------------------
++
++To use the LDAP authentification, use the following simple tacacs+ configuration file
++
++ key = "your tacacs key"
++ accounting file = /var/log/tac-plus/tac_plus.log
++ default authentification = ldap "ldap://"
++ user=DEFAULT {
++ service = ppp protocol = ip {
++ }
++ }
++
++
++For more information on the configuration file please use the complete tacacs+ documentation.
++
++
++How to start the tacacs+ daemon
++-------------------------------
++
++Make sure your LD_LIBRARY_PATH includes the LDAP libraries.
++As root, start the tacacs daemon:
++ # /usr/local/src/tac_plus.F4.0.3-v8.alpha/tac_plus -C tac_plus.cfg
++
++
++How to configure the LDAP server
++--------------------------------
++
++a) Notes Domino LDAP server
++---------------------------
++
++You have to enable the Domino server task "LDAP" with the Administration Tool. You
++can do this with the command "laod ldap" at the server console or with the help of
++the Tools Menu of the server tab (Tools -> Task -> Start "LDAP Server").
++
++You can define which attributes of your Domino Directory are accessible by
++anonymous users and if it is allowed to write to your Domino Directory using LDAP in
++a Configuration document. You have to specify "Use these settings as the default
++settings for all servers" in the Basic tab of the Configuration document to display
++the LDAP options tab. There you are able to adjust the settings for a your LDAP server.
++
++For additional information see the IBM Red Book "Getting the most from your Domino
++Directory" (11/2000), which you can downlaod from http://www.redbooks.ibm.com.
++
++
++b) Open LDAP
++------------
++
++It is also possible to use OpenLDAP for this kind of authentification. Please look at
++the documentation at http://www.openldap.org for details how to install the server.
++
++
++Security
++---------
++
++The here described tacacs+ queries are not quering any of the fields stored in your LDAP
++server. We only try to log in and this is the "test" we perform here.
++
++Pleae note that the passwords are not send encrypted. You have to make sure that it is
++not possible to sniff them. In general is there no support from tacacs+ to support encrypted
++passwords.
++It is maybe possible to use OpenLDAP with TLS support to encrypt the passwords and use a
++secure LDAP server. This is also supported by Domino and OpenLDAP. But this is not implemented.
++
++Good luck,
++
++ Harpes Patrick (patrick.harpes@tudor.lu) and Jahnen Andreas (andreas.jahnen@tudor.lu)
+diff --git a/README.PAM b/README.PAM
+index 0c1d468..7cbd4b4 100644
+--- a/README.PAM
++++ b/README.PAM
+@@ -36,5 +36,3 @@ into tac_plus.conf.
+
+
+ Max Liccardo
+-
+-
+diff --git a/acconfig.h b/acconfig.h
+new file mode 100644
+index 0000000..72e7998
+--- /dev/null
++++ b/acconfig.h
+@@ -0,0 +1,97 @@
++/* acconfig.h
++ *
++ * $Id$
++ */
++
++#ifndef TAC_PLUS_CONFIG_H
++#define TAC_PLUS_CONFIG_H 1
++
++@TOP@
++
++
++/* --maintainer-mode
++ * Sets "/etc/tacacs/tac_plus.cfg" as default config file
++ */
++#undef MAINTAINER_MODE
++
++/* Missing socklen_t in
++ * We don't use 'typedef' to not to yet require included here.
++ */
++#undef socklen_t
++
++/* Define this if you have shadow passwords in /etc/passwd and
++ * /etc/shadow. Note that you usually need to be root to read
++ * /etc/shadow */
++#undef SHADOW_PASSWORDS
++
++/* Check for some fields in /struct passwd and /struct utmp
++ */
++#undef HAVE_PASSWD_PW_AGE
++#undef HAVE_PASSWD_PW_COMMENT
++#undef HAVE_UTMP_UT_HOST
++
++/* All OSes detected by configure.in:
++ */
++#undef LINUX
++#undef GLIBC
++#undef SOLARIS
++#undef FREEBSD
++#undef HPUX
++#undef AIX
++#undef MIPS
++
++/* --with-pam */
++#undef USE_PAM
++/* --with-ldap */
++#undef USE_LDAP
++/* --with-db */
++#undef DB
++/* --with-db */
++#undef DB_NULL
++/* --with-mysql */
++#undef DB_MYSQL
++/* --with-pgsql */
++#undef DB_PGSQL
++/* --enable-maxsess */
++#undef MAXSESS
++/* --with-libwrap */
++#undef TCPWRAPPER
++/* --with-skey */
++#undef SKEY
++/* --with-mschap[=des] */
++#undef MSCHAP
++#undef MSCHAP_DES
++/* --with-tac[ug]id */
++#undef TACPLUS_USERID
++#undef TACPLUS_GROUPID
++/* --with-tacplus_pid */
++#undef TACPLUS_PIDFILE
++/* --with-included-regex */
++#undef WITH_INCLUDED_REGEX
++
++
++@BOTTOM@
++
++/* Keep in sync with configure.in */
++#define _XOPEN_SOURCE 1 /* for unistd.h/crypt() */
++#define _XOPEN_SOURCE_EXTENDED 1 /* for pwd.h/{set,end}pwent(), sys/wait.h/wait3() */
++#define _BSD_SOURCE 1 /* for u_{char,short,int} & string.h/bcopy() */
++#define _OSF_SOURCE 1 /* for u_{char,short,int} (on Alpha OSF1) */
++#define __EXTENSIONS__ 1 /* for u_{char,short,int} (on Sparc Solaris) */
++
++#if SIZEOF_UNSIGNED_SHORT == 4
++typedef unsigned short tac_uint32;
++#else
++#if SIZEOF_UNSIGNED_INT == 4
++typedef unsigned int tac_uint32;
++#else
++#if SIZEOF_UNSIGNED_LONG == 4
++typedef unsigned long tac_uint32;
++#else
++#error "Unable to find 32-bit unsigned int for TAC_UINT32 type"
++#endif /* SIZEOF_UNSIGNED_LONG */
++#endif /* SIZEOF_UNSIGNED_INT */
++#endif /* SIZEOF_UNSIGNED_SHORT */
++
++
++#endif /* TAC_PLUS_CONFIG_H */
+diff --git a/acct.c b/acct.c
+index 315064f..2145cef 100644
+--- a/acct.c
++++ b/acct.c
+@@ -17,18 +17,37 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include
++#include /* for ntohl() */
++
++#include "acct.h"
++#include "report.h"
++#include "packet.h"
++#include "utils.h"
++#include "do_acct.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++#include "cfgfile.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++#ifdef DB
++#include "db.h"
++#endif
++
++
++static void account TAC_ARGS((u_char *pak));
++
++
+ /*
+ * Come here when we receive an Start Accounting packet
+ */
+
+-void account();
+-
+-/* For DB accounting */
+-#ifdef DB
+-int db_acct();
+-#endif /* DB */
++void accounting TAC_ARGS((u_char *pak));
+
+ void
+ accounting(pak)
+@@ -37,8 +56,8 @@ u_char *pak;
+ struct acct *acct_pak;
+ u_char *p;
+ HDR *hdr;
+- u_char *read_packet();
+- int i, len;
++ int i;
++ unsigned long len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "Start accounting request");
+@@ -59,7 +78,7 @@ u_char *pak;
+ len += p[i];
+ }
+
+- if (len != ntohl(hdr->datalength)) {
++ if (len != (unsigned long) ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_ACCT, NULL);
+ return;
+ }
+@@ -69,7 +88,9 @@ u_char *pak;
+ free(pak);
+ }
+
+-void
++static void account TAC_ARGS((u_char *pak));
++
++static void
+ account(pak)
+ u_char *pak;
+ {
+@@ -123,6 +144,8 @@ u_char *pak;
+
+ identity.priv_lvl = acct_pak->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ rec.identity = &identity;
+
+ /* Now process cmd args */
+diff --git a/acct.h b/acct.h
+new file mode 100644
+index 0000000..0640ae6
+--- /dev/null
++++ b/acct.h
+@@ -0,0 +1,12 @@
++#ifndef ACCT_H
++#define ACCT_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++extern void accounting TAC_ARGS((u_char *pak));
++
++
++#endif /* ACCT_H */
+diff --git a/authen.c b/authen.c
+index a12745e..7c873fe 100644
+--- a/authen.c
++++ b/authen.c
+@@ -17,16 +17,43 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
+-static int choose();
+-static void authenticate();
+-static void do_start();
++#include
++#include /* for ntohl() */
++
++#include "authen.h"
++#include "packet.h"
++#include "report.h"
++#include "utils.h"
++#include "choose_authen.h"
++#include "do_author.h" /* for "struct identity" */
++#include "main.h"
++#include "cfgfile.h"
++
++#ifdef TCPWRAPPER
++#include "tcpwrap.h"
++#endif
++
++
++static void do_start TAC_ARGS((u_char *pak));
++static int choose TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++static void authenticate TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
++
++/* Configurable:
++ */
++
++#define TAC_PLUS_MAX_ITERATIONS 50
++
+
+ /*
+ * Come here when we receive an authentication START packet
+ */
+
++void authen TAC_ARGS((u_char *pak));
++
+ void
+ authen(pak)
+ u_char *pak;
+@@ -39,9 +66,9 @@ u_char *pak;
+ start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
+
+ if ((hdr->seq_no != 1) ||
+- (ntohl(hdr->datalength) != TAC_AUTHEN_START_FIXED_FIELDS_SIZE +
++ ((unsigned long) ntohl(hdr->datalength) != (unsigned long)(TAC_AUTHEN_START_FIXED_FIELDS_SIZE +
+ start->user_len + start->port_len + start->rem_addr_len +
+- start->data_len)) {
++ start->data_len))) {
+ send_authen_error("Invalid AUTHEN/START packet (check keys)");
+ return;
+ }
+@@ -65,6 +92,8 @@ u_char *pak;
+ * attempt to authenticate.
+ */
+
++static void do_start TAC_ARGS((u_char *pak));
++
+ static void
+ do_start(pak)
+ u_char *pak;
+@@ -109,6 +138,8 @@ u_char *pak;
+
+ identity.priv_lvl = start->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ /* The authen_data structure */
+
+ bzero(&authen_data, sizeof(struct authen_data));
+@@ -184,6 +215,8 @@ send_authen_error("You are not allowed to access here");
+ /* Choose an authentication function. Return 1 if we successfully
+ chose a function. 0 if we couldn't make a choice for some reason */
+
++static int choose TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
+ static int
+ choose(datap, typep)
+ struct authen_data *datap;
+@@ -293,6 +326,8 @@ struct authen_type *typep;
+ /* NOTREACHED */
+ }
+
++static void authenticate TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
+ /* Perform authentication assuming we have successfully chosen an
+ authentication method */
+ static void
+@@ -303,7 +338,7 @@ struct authen_type *typep;
+ int iterations = 0;
+ u_char *reply, *p;
+ struct authen_cont *cont;
+- int (*func) ();
++ int (*func) TAC_ARGS((struct authen_data *data));
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Calling authentication function");
+@@ -469,4 +504,3 @@ struct authen_type *typep;
+ /* NOTREACHED */
+ }
+ }
+-
+diff --git a/authen.h b/authen.h
+new file mode 100644
+index 0000000..d89d9cc
+--- /dev/null
++++ b/authen.h
+@@ -0,0 +1,12 @@
++#ifndef AUTHEN_H
++#define AUTHEN_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++extern void authen TAC_ARGS((u_char *pak));
++
++
++#endif /* AUTHEN_H */
+diff --git a/author.c b/author.c
+index a9f2277..7a05db5 100644
+--- a/author.c
++++ b/author.c
+@@ -17,12 +17,27 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include
++#include /* for ntohl() */
++
++#include "author.h"
++#include "report.h"
++#include "packet.h"
++#include "utils.h"
++#include "do_author.h"
++#include "main.h"
++#include "cfgfile.h"
++
++
+ /*
+ * Come here when we receive an authorization START packet
+ */
+
++void author TAC_ARGS((u_char *pak));
++
+ void
+ author(pak)
+ u_char *pak;
+@@ -34,7 +49,8 @@ u_char *pak;
+ u_char *p;
+ u_char *argsizep;
+ char **cmd_argp;
+- int i, len;
++ int i;
++ unsigned long len;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Start authorization request");
+@@ -58,7 +74,7 @@ u_char *pak;
+ len += p[i];
+ }
+
+- if (len != ntohl(hdr->datalength)) {
++ if (len != (unsigned long) ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_AUTHOR, NULL);
+ return;
+ }
+@@ -95,6 +111,8 @@ u_char *pak;
+
+ identity.priv_lvl = apak->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ /* The author_data structure */
+
+ author_data.id = &identity; /* user id */
+diff --git a/author.h b/author.h
+new file mode 100644
+index 0000000..0e3a2ce
+--- /dev/null
++++ b/author.h
+@@ -0,0 +1,12 @@
++#ifndef AUTHOR_H
++#define AUTHOR_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++extern void author TAC_ARGS((u_char *pak));
++
++
++#endif /* AUTHOR_H */
+diff --git a/autogen b/autogen
+new file mode 100755
+index 0000000..83862fd
+--- /dev/null
++++ b/autogen
+@@ -0,0 +1,47 @@
++#! /bin/sh
++#
++# autogen
++#
++# Copyright (C) 1999, 2000, 2001
++# Partition Surprise Team
++#
++# $Id$
++#
++
++
++# Run this to generate all the initial makefiles, etc.
++
++set -e
++if test "x$1" = "xBASH" ;then
++ shift
++else if test "x$BASH" = "x" ;then
++ for trypath in `echo "$PATH"|tr : ' '` /usr/local/bin;do
++ if test -x "$trypath/bash";then
++ "$trypath/bash" "$0" BASH "$@"
++ exit $?
++ fi
++ done
++ echo "ERROR: Unable to find 'bash' interpreter needed for self-execution!"
++ exit 1
++fi;fi
++
++defaultCONFDEFS="" # --enable-debug --without-efence
++project="tac_plus"
++automake_gnu=false
++want_tarZ=false
++want_gettext=false
++want_libtool=false
++#upload="vellum.cz:WWW/sw/"
++docdir=""
++subdirs=""
++
++CLEAN_LOCAL="
++ .print_userprogs
++ tac_plus
++ generate_passwd
++"
++
++ARGS_HELP_LOCAL="\
++"
++
++source ./autogen-body
+diff --git a/autogen-body b/autogen-body
+new file mode 100644
+index 0000000..6856444
+--- /dev/null
++++ b/autogen-body
+@@ -0,0 +1,259 @@
++#
++# autogen-body
++#
++# Placed into the public domain by
++# Partition Surprise Team
++#
++# $Id$
++#
++
++
++# Executable code to be included from ./autogen script
++
++# Expected variables:
++# defaultCONFDEFS, project, want_tarZ, upload, docdir, subdirs, CLEAN_LOCAL
++# function PREP_LOCAL
++
++set -e
++t=/tmp/autogen.$$
++autogen_failed=true
++cleaup_dir="$PWD"
++automake_reqd=""
++function cleanup
++{
++ cd "$cleanup_dir"
++ rm -f "$t.*"
++ if $automake_gnu;then for i in $automake_reqd;do if [ '!' -s "$i" ];then rm -f "$i";fi;done;fi
++}
++EXITmsg_do=true
++trap '
++ cleanup
++ if $autogen_failed;then
++ if $EXITmsg_do;then
++ echo -e "\n$0 failed! Try the following command to debug it: set -x"
++ EXITmsg_do=false
++ fi
++ exit 1
++ fi
++ ' EXIT
++
++if [ "$1" = help -o "$1" = -h -o "$1" = --help ];then cat </dev/null;then
++local func_exit=false
++
++ "$func" "$@"
++ if $func_exit;then exit;fi
++ fi
++}
++funcdo ARGS_LOCAL "$@"
++
++if expr match "$1" "rpm" >/dev/null;then
++ builds="/usr/src/redhat /usr/src/packages"
++ for b in $builds X;do
++ if test -d $b;then break;fi
++ done
++ if [ $b = X ];then
++ echo Build directory not reachable, searched: $builds
++ exit 1
++ fi
++ rm -r -f /var/tmp/$project-*-root $b/BUILD/$project-*
++ specsrc="$project.spec.m4.in"
++ if [ '!' -f "$specsrc" ];then specsrc="$project.spec.in";fi
++ CONFDEFS="`awk '/^(.*)\\$/{x=x$1" ";next}{print x$0;x=""}' <$specsrc \
++ |sed -n 's/^.*\.\/configure \(.*\)$/\1/p'`" ./autogen copy
++ make dist $project.spec
++ cp $project-*.tar.gz $b/SOURCES
++ if [ "$1" = "rpmtest" ];then SIGNIT=;else SIGNIT=--sign;fi
++ rpm -ba $SIGNIT $project.spec
++ if $want_tarZ;then make dist-tarZ;fi
++ rm $b/SOURCES/$project-*.tar.gz
++ mv $b/SRPMS/$project-*.src.rpm .
++ mv $b/RPMS/i386/$project-*.i386.rpm .
++ ls -l $project-*
++ if [ "$1" = rpmup ];then
++ echo "Uploading $[`cat $project-*|wc -c`] bytes..."
++ if [ -n "$upload" ];then
++ scp -v $project-* "$upload"
++ else
++ echo "Upload not done."
++ fi
++ fi
++ autogen_failed=false
++ exit
++fi
++
++function subdo
++{
++ for i in _ $subdirs;do
++ if [ -d $i ];then
++ cd "$i"
++ ./autogen $subdir_args
++ cd ..
++ fi
++ done
++}
++subdir_args="${*:-dist}"
++if [ "$subdir_args" = "copy" ];then
++ subdir_args="copy dist"
++ fi
++
++# maintainer-clean hack is not safe, please list all files for 'rm'.
++# When the filename doesn't contain '/', it is applied to ALL directories.
++# Please note that files exactly in root dir MUST have ./ in the front
++# (to not to be considered as ALL-directories files).
++
++CLEANFILES="
++ *~ .#*
++ *.orig *.rej
++ core
++ Makefile Makefile.in
++ TAGS tags ID
++ .deps .libs
++ *.[oa] *.l[oa]
++
++ ./errs*
++ ./intl
++ ./configure ./configure.scan
++ ./config.guess ./config.status ./config.sub ./config.log ./config.cache
++ ./config.h ./config.h.in
++ ./confdefs.h ./conftest* ./autoh[0-9]* ./confcache
++ ./stamp-h ./stamp-h.in
++ ./install-sh
++ ./aclocal.m4
++ ./missing
++ ./mkinstalldirs
++ ./libtool ./ltconfig ./ltmain.sh
++ ./ChangeLog
++ ./ABOUT-NLS ./COPYING
++ ./$project-[0-9]* ./$project-devel-[0-9]*
++ ./$project.spec ./$project.m4 ./$project.spec.m4
++ macros/macros.dep
++ po/Makefile.in.in po/POTFILES* po/cat-id-tbl.c po/cat-id-tbl.tmp po/*.gmo po/*.mo po/stamp-cat-id po/$project.pot
++
++ $CLEAN_LOCAL
++ "
++if [ -n "$docdir" ];then
++CLEANFILES="
++ $docdir/*.html
++ $docdir/*.info*
++ $docdir/*.txt
++ $docdir/*.tex
++ $docdir/*.sgml
++ $CLEANFILES"
++ fi
++if [ "$1" != sym ];then CLEANFILES="$CLEANFILES `find . -type l`";fi
++CLEANFILES="`echo "$CLEANFILES"|tr ' ' '\n'|sed 's,^\./\(.*/.*\)$,\1,'|sort|uniq|grep -v '^ *$'`"
++true >"$t.find"
++rm -f "$t.discard"
++echo "$CLEANFILES"|while read -r fi;do
++ if [ "$fi" != "${fi#*/}" ];then
++ echo "$fi" >>"$t.discard"
++ else echo "-o -name $fi" >>"$t.find"
++ fi;done
++for dirpatt in `(find . -type d '!' \( -name CVS $(sed 's,[]*?[],\\&,g' <"$t.find") \)|sort|uniq;cat "$t.discard")|sort|uniq -u`;do
++ for dir in $dirpatt;do if test -d $dir;then
++ (cd $dir
++ (echo "$CLEANFILES" #ALL-dir files
++ echo "$CLEANFILES"|sed -n "s,^\\$(echo $dir|sed 's,^\./,,')/,,p" #THIS-dir files
++ echo .cvsignore #MUST be last!
++ )|grep -v / >.cvsignore
++ if [ "$1" = "${1#tar}" ];then
++ rm -rf `
++ if [ "$1" = fullclean ];then cat .cvsignore
++ else grep -v '^\.cvsignore' <.cvsignore
++ fi`
++ fi
++ )
++ fi;done
++ done
++rm -f "$t.find" "$t.discard"
++if [ "$1" = tarprep ];then
++ autogen_failed=false
++ exit
++ fi
++if [ "$1" = tar ];then
++ subdir_args=tarprep
++ subdo
++ mydir="$(basename `pwd`)"
++ cd ..
++ tar cf - \
++ $(for fi in `find "$mydir" -name .cvsignore`;do sed "s,^,--exclude=`dirname $fi`/," <$fi;done) \
++ "$mydir"
++ autogen_failed=false
++ exit
++ fi
++if [ "$1" != "${1#*clean}" ];then
++ subdo
++ autogen_failed=false
++ exit 0
++ fi
++
++funcdo PREP_LOCAL "$@"
++subdo
++
++if [ "$1" = copy ];then COPY=--copy;shift
++else unset COPY|cat # |cat construction is used to not fail in "set -e" state
++fi
++
++if test -d po;then
++ touch po/POTFILES.in
++fi
++aclocal_opts=""
++if test -d macros;then
++ aclocal_opts="-I macros $aclocal_opts"
++fi
++aclocal $aclocal_opts
++if $want_gettext;then
++ gettextize $COPY
++ rm -f aclocal.m4 # We delete created aclocal.m4 as it's just bug in gettextize. It shouldn't link that file here
++ aclocal $aclocal_opts # gettextize made some changes of files which need to be reflected
++fi
++if $want_libtool;then
++ libtoolize $COPY
++fi
++autoheader
++automake_opts=""
++if $automake_gnu;then
++ automake_reqd="$automake_reqd ChangeLog README"
++ automake_opts="$automake_opts --gnu"
++ for i in $automake_reqd;do if [ '!' -f "$i" ];then touch "$i";fi;done
++fi
++automake --add-missing $COPY $automake_opts
++cleanup
++autoheader
++autoconf
++
++if [ "$1" != dist ];then
++ # shared/static switching cannot be based on maintainer-mode in configure
++ ./configure --enable-maintainer-mode --enable-shared --disable-static $CONFDEFS
++ fi
++
++autogen_failed=false
+diff --git a/cfgeval.c b/cfgeval.c
+new file mode 100644
+index 0000000..f0aed72
+--- /dev/null
++++ b/cfgeval.c
+@@ -0,0 +1,1900 @@
++/*
++ * ???():
++ * If you belong to the group where do you belong only in the case you belong
++ * in that group, do you in fact belong to that group or don't?
++ * I've decided it is safer to decide that you DON'T belong to such group.
++ *
++ * expr_eval_notify_expr_remove():
++ * If someone was interested in what am I like but she is no longer
++ * interested in what am I like, because she already knows what is she like,
++ * then she should tell it me, as although I still may not know what am I
++ * like then in the case she was the last who wanted to know what am I like,
++ * I should tell everyone who I didn't know what they are like and I wanted
++ * to know what they are like that I no longer want to know what they are
++ * like, because it is no longer needed to know what am I like about myself.
++ *
++ * membership_eval_immediate():
++ * It is important to not only know, what do you want to know, but it is also
++ * important to know what do you now know but you still didn't utilize it.
++ */
++
++
++#include "tac_plus.h"
++
++#include
++
++#include "cfgeval.h"
++#include "cfgfile.h"
++#include "main.h"
++#include "parse.h"
++#include "report.h"
++#include "hash.h"
++
++
++/* Configurable:
++ */
++
++/* Whether to do sanity checks */
++#define SCAN_PARANOIA 1
++
++/* report even no-op scan up-to-date checks */
++#define REPORT_CHECK_SCAN_VERBOSE 0
++
++
++static void check_request_scan_membership TAC_ARGS((struct membership *membership, int flush));
++static void check_eval_scan_membership TAC_ARGS((struct membership *membership, int flush));
++static void check_eval_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++static void check_request_scan_expr TAC_ARGS((struct expr *expr, int flush));
++static void check_eval_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++
++static unsigned request_scan_seq = 0;
++static unsigned value_scan_seq = 0;
++static unsigned eval_scan_seq = 0;
++int request_scan_user_known;
++static struct tac_list eval_kicked_entity_list;
++static struct tac_list eval_destroy_entity_list;
++static struct tac_list eval_notified_expr_list;
++static struct tac_list request_virtual_membership_list;
++
++
++/* 'P[FA]_*' object printing section:
++ */
++
++static const char *eval_result_to_string TAC_ARGS((int valid, enum eval_result er));
++
++const char *
++eval_result_to_string(valid, er)
++int valid;
++enum eval_result er;
++{
++ if (!valid)
++ return ("!UPDATED");
++
++ switch (er) {
++ case ER_TRUE: return ("ER_TRUE" );
++ case ER_FALSE: return ("ER_FALSE" );
++ case ER_UNKNOWN: return ("ER_UNKNOWN" );
++ default: return ("*** INVALID ***");
++ }
++ /* NOTREACHED */
++}
++
++static const char *value_scan_func_result_to_string TAC_ARGS((enum value_scan_func_result vsfr));
++
++const char *
++value_scan_func_result_to_string(vsfr)
++enum value_scan_func_result vsfr;
++{
++ switch (vsfr) {
++ case VSFR_CONTINUE: return ("VSFR_CONTINUE");
++ case VSFR_FOUND: return ("VSFR_FOUND" );
++ case VSFR_STOP: return ("VSFR_STOP" );
++ default: return ("*** INVALID ***");
++ }
++ /* NOTREACHED */
++}
++
++/* These macros are VERY big overhead but it's primary negative effect
++ * is the size of executable. I've considered it as not much interesting,
++ * CPU overhead is hit only when DEBUG_CFGEVAL_FLAG (also not interesting).
++ */
++
++#define PF_VSFR "%s"
++#define PA_VSFR(vsfr) (value_scan_func_result_to_string((vsfr)))
++
++#define PF_RESULT "%s"
++#define PA_RESULT(result) (eval_result_to_string(1 /* valid */, (result)))
++
++#define PF_ERESULT_struct PF_RESULT
++#define PA_ERESULT_struct(entity, field) \
++ (!(entity) ? "*NULL*(=ER_TRUE)" : \
++ (eval_result_to_string((entity)->request_scan.seq == request_scan_seq, (entity)->request_scan.field)))
++#define PA_ERESULT_struct_NULL "*NULL*"
++
++#define PF_ERESULT_EXPR PF_ERESULT_struct
++#define PA_ERESULT_EXPR(expr) PA_ERESULT_struct((expr), result)
++#define PF_ERESULT_ENTITY PF_ERESULT_struct
++#define PA_ERESULT_ENTITY(entity) PA_ERESULT_struct((entity), belongs)
++#define PF_ERESULT_MEMBERSHIP PF_ERESULT_struct
++#define PA_ERESULT_MEMBERSHIP(membership) (!(membership) ? PA_ERESULT_struct_NULL : PA_ERESULT_EXPR((membership)->when))
++
++#define PF_EXPR_ "expr@%d{%s " PF_MEMBERSHIP "}"
++#define PA_EXPR_(expr) (!(expr) ? 0 : (expr)->line), \
++ (!(expr) ? "*NULL*" : "of"), \
++ PA_MEMBERSHIP((!(expr) ? NULL : (expr)->membership))
++
++#define PF_MEMBERSHIP_ "membership{child=" PF_ENTITY ",parent=" PF_ENTITY "}"
++#define PA_MEMBERSHIP_(membership) PA_ENTITY((!(membership) ? NULL : MEMBERSHIP_TO_CHILD_ENTITY( (membership)))), \
++ PA_ENTITY((!(membership) ? NULL : MEMBERSHIP_TO_PARENT_ENTITY((membership))))
++
++#define PF_ENTITY_ "{%s@%d \"%s\"}"
++#define PA_ENTITY_(entity) (!(entity) ? "*NULL* entity" : entity_type_to_string((entity)->type)), \
++ (!(entity) ? 0 : (entity)->line), \
++ (!(entity) ? "*NULL*" : (entity)->name)
++
++#define PF_EXPR PF_EXPR_ "=" PF_ERESULT_EXPR
++#define PA_EXPR(expr) PA_EXPR_(expr), PA_ERESULT_EXPR(expr)
++#define PF_MEMBERSHIP PF_MEMBERSHIP_ "=" PF_ERESULT_MEMBERSHIP
++#define PA_MEMBERSHIP(membership) PA_MEMBERSHIP_(membership), PA_ERESULT_MEMBERSHIP(membership)
++#define PF_ENTITY PF_ENTITY_ "=" PF_ERESULT_ENTITY
++#define PA_ENTITY(entity) PA_ENTITY_(entity), PA_ERESULT_ENTITY(entity)
++
++
++/* '{unlink,free}_*()' section:
++ */
++
++void unlink_expr TAC_ARGS((struct expr *expr));
++
++/* never depend on "->parent" as it may not yet been filled! */
++void
++unlink_expr(expr)
++struct expr *expr;
++{
++ if (!expr)
++ return; /* prevent possible DEBUG_CLEAN_FLAG report */
++
++ if (debug & DEBUG_CLEAN_FLAG) {
++ if (expr->membership)
++ report(LOG_DEBUG, "unlink_expr: (of membership): " PF_EXPR,
++ PA_EXPR(expr));
++ else
++ report(LOG_DEBUG, "unlink_expr: (standalone)");
++ }
++
++ while (expr) {
++
++ /* We need to at least unlink "eval_scan->u.entity.notify_expr_node": */
++ check_request_scan_expr(expr, 1); /* invalidate */
++ check_eval_scan_expr(expr, 1); /* invalidate */
++
++ switch (expr->type) {
++
++ case S_not:
++ unlink_expr(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ unlink_expr(expr->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for unlink_expr", expr->type);
++ return;
++ }
++
++ expr = expr->next;
++ }
++}
++
++void free_expr TAC_ARGS((struct expr *expr));
++
++/* given 'expr' memory WILL be freed! */
++/* never depend on "->parent" as it may not yet been filled! */
++void
++free_expr(expr)
++struct expr *expr;
++{
++struct expr *expr_next;
++
++ if (!expr)
++ return; /* prevent possible DEBUG_CLEAN_FLAG report */
++
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "free_expr: (may be unlinked)");
++
++ while (expr) {
++ expr_next = expr->next;
++ switch (expr->type) {
++
++ case S_not:
++ free_expr(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ free_expr(expr->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ if (expr->u.entity.name)
++ free((/* de-const */ char *)expr->u.entity.name);
++ /* "expr->u.entity.entity" will be freed from elsewhere */
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for free_expr", expr->type);
++ return;
++ }
++
++ free(expr);
++ expr=expr_next;
++ }
++}
++
++static void unlink_membership TAC_ARGS((struct membership *membership));
++
++static void
++unlink_membership(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "unlink_membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ /* 'unlink_expr()' may want a lot of existing (linked) resources */
++ unlink_expr(membership->when);
++
++ check_request_scan_membership(membership, 1); /* invalidate */
++ check_eval_scan_membership(membership, 1); /* invalidate */
++
++#ifdef SCAN_PARANOIA
++ if (!parent_entity->to_child_membership_num) {
++ report(LOG_ERR, "INTERNAL: to_child_membership_num-- == 0 in unlink_membership");
++ parent_entity->to_child_membership_num++;
++ }
++#endif
++ parent_entity->to_child_membership_num--;
++
++ tac_list_node_remove(&membership->parent_node);
++ tac_list_node_remove(&membership-> child_node);
++}
++
++/* given 'membership' memory WILL be freed! */
++
++static void free_membership TAC_ARGS((struct membership *membership));
++
++static void
++free_membership(membership)
++struct membership *membership;
++{
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "free_membership: (may be unlinked)");
++
++ free_expr(membership->when);
++ free(membership);
++}
++
++/* we are not allowed to free memory here, we are only 'scan_' additional 'free' */
++
++void scan_free_entity TAC_ARGS((ENTITY *entity));
++
++void
++scan_free_entity(entity)
++ENTITY *entity;
++{
++ struct tac_list_node *parent_membership_node, *next_parent_membership_node;
++ struct membership *parent_membership;
++ struct tac_list_node *child_membership_node, *next_child_membership_node;
++ struct membership *child_membership;
++
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "scan_free_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ /* Be careful to keep '->next' ptr before we destroy the structure! */
++
++ for (
++ parent_membership_node = tac_list_first_node(&entity->to_child_membership_list);
++ parent_membership_node;
++ parent_membership_node = next_parent_membership_node
++ ) {
++ parent_membership = PARENT_NODE_TO_MEMBERSHIP(parent_membership_node);
++ next_parent_membership_node = tac_list_node_next(parent_membership_node);
++ unlink_membership(parent_membership);
++ free_membership(parent_membership);
++ }
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ child_membership_node;
++ child_membership_node = next_child_membership_node
++ ) {
++ child_membership = CHILD_NODE_TO_MEMBERSHIP(child_membership_node);
++ next_child_membership_node = tac_list_node_next(child_membership_node);
++ unlink_membership(child_membership);
++ free_membership(child_membership);
++ }
++}
++
++struct expr *new_expr TAC_ARGS((int type));
++
++struct expr *
++new_expr(type)
++int type;
++{
++ struct expr *expr;
++
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->next = NULL;
++ expr->type = type;
++ switch (expr->type) {
++
++ case S_not:
++ expr->u.not.child = NULL;
++ break;
++
++ case S_and:
++ case S_or:
++ expr->u.and_or.child_first = NULL;
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr->u.entity.entity = NULL;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for new_expr", expr->type);
++ return (expr); /* it would be probably lethal to return NULL */
++ }
++ return (expr);
++}
++
++static int expr_sink_internal TAC_ARGS((struct expr *expr, struct membership *membership, struct expr *parent));
++
++static int
++expr_sink_internal(expr, membership, parent)
++struct expr *expr;
++struct membership *membership;
++struct expr *parent;
++{
++ for (;expr; expr=expr->next) {
++ expr->membership = membership;
++ expr->parent = parent;
++ expr->request_scan.seq = request_scan_seq-1;
++ expr-> eval_scan.seq = eval_scan_seq-1;
++ switch (expr->type) {
++
++ case S_not:
++ if (expr_sink_internal(expr->u.not.child, membership, expr /* parent */))
++ return (1);
++ break;
++
++ case S_and:
++ case S_or:
++ if (expr_sink_internal(expr->u.and_or.child_first, membership, expr /* parent */))
++ return (1);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ tac_list_node_init(&expr->eval_scan.u.entity.notify_expr_node);
++ expr->u.entity.entity = entity_lookup(expr->type, expr->u.entity.name);
++ if (!expr->u.entity.entity && expr->type == S_host) {
++ expr->u.entity.entity = new_entity(expr->type, (char *)expr->u.entity.name, expr->line);
++ if (!expr->u.entity.entity)
++ return (1);
++ expr->u.entity.name = NULL;
++ }
++ if (!expr->u.entity.entity) {
++ report(LOG_ERR, "referenced entity %s %s not found on line %d",
++ entity_type_to_string(expr->type), expr->u.entity.name, expr->line);
++ return (1);
++ }
++ /* already NULLed for not-yet-existing S_host */
++ free((char *) expr->u.entity.name);
++ expr->u.entity.name = NULL;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for expr_sink", expr->type);
++ return (1);
++ }
++ }
++ return (0);
++}
++
++static int expr_sink TAC_ARGS((struct expr *expr, struct membership *membership));
++
++static int
++expr_sink(expr, membership)
++struct expr *expr;
++struct membership *membership;
++{
++ return (expr_sink_internal(expr, membership, NULL /* parent */));
++}
++
++static struct expr *expr_sink_register_head = NULL;
++
++void expr_sink_register TAC_ARGS((struct expr *expr));
++
++void
++expr_sink_register(expr)
++struct expr *expr;
++{
++ if (!expr)
++ return;
++
++ expr->parent = expr_sink_register_head;
++ expr_sink_register_head = expr;
++}
++
++int expr_sink_commit TAC_ARGS((void));
++
++int
++expr_sink_commit()
++{
++ struct expr *expr;
++
++ while ((expr = expr_sink_register_head)) {
++ expr_sink_register_head = expr->parent;
++ /* 'expr->parent' not defined for 'expr_sink()' */
++
++ if (expr_sink(expr, NULL /* membership */))
++ return (1); /* failure */
++ }
++ return (0); /* success */
++}
++
++struct expr *dupl_expr TAC_ARGS((const struct expr *in));
++
++struct expr *
++dupl_expr(in)
++const struct expr *in;
++{
++ struct expr *expr_root = NULL;
++ struct expr **succ_exprp = &expr_root;
++ struct expr *expr;
++
++ for (;in; in=in->next) {
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = in->line;
++ expr->next = NULL;
++ expr->type = in->type;
++ switch (in->type) {
++
++ case S_not:
++ if (in->u.not.child && in->u.not.child->type==S_not) {
++ free(expr);
++ expr = dupl_expr(in->u.not.child->u.not.child);
++ } else
++ expr->u.not.child = dupl_expr(in->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ if (!in->u.and_or.child_first) {
++ free(expr);
++ continue;
++ } else if (!in->u.and_or.child_first->next) {
++ free(expr);
++ expr = dupl_expr(in->u.and_or.child_first);
++ } else
++ expr->u.and_or.child_first = dupl_expr(in->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ if (in->u.entity.name)
++ expr->u.entity.name = tac_strdup(in->u.entity.name);
++ else
++ expr->u.entity.name = NULL;
++ expr->u.entity.entity = in->u.entity.entity;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for dupl_expr", in->type);
++ free_expr(expr_root);
++ return (NULL);
++ }
++
++ *succ_exprp = expr;
++ succ_exprp = &expr->next;
++ }
++ return (expr_root);
++}
++
++
++/* 'check_*_scan_*()' section:
++ */
++
++static void check_request_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++static void
++check_request_scan_expr(expr, flush)
++struct expr *expr;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_expr: " PF_EXPR " (" PF_ERESULT_EXPR ")",
++ PA_EXPR(expr), PA_ERESULT_EXPR(expr));
++#endif
++
++ if (!flush && expr->request_scan.seq == request_scan_seq)
++ return; /* up to date */
++
++ expr->request_scan.result = ER_UNKNOWN;
++ expr->request_scan.loop_reported = 0;
++ expr->request_scan.seq = request_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_expr: done: " PF_EXPR,
++ PA_EXPR(expr));
++}
++
++static void check_eval_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++static void
++check_eval_scan_expr(expr, flush)
++struct expr *expr;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_expr: " PF_EXPR,
++ PA_EXPR(expr));
++#endif
++
++ if (!flush && expr->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_request_scan_expr(expr, 0);
++
++ switch (expr->type) {
++
++ case S_user:
++ case S_host:
++ case S_group: {
++#ifdef SCAN_PARANOIA
++ if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node) == &eval_notified_expr_list) {
++ report(LOG_ERR, "INTERNAL: expr still connected to eval_notified_expr_list in check_eval_scan_expr");
++ } else if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node)) {
++ ENTITY *notifying_entity = EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr);
++
++ if (notifying_entity != expr->u.entity.entity)
++ report(LOG_ERR, "INTERNAL: expr->notify_expr_node->list != expr->entity");
++ if (notifying_entity->eval_scan.seq != expr->eval_scan.seq)
++ report(LOG_ERR, "INTERNAL: entity seq != expr node seq");
++ tac_list_node_remove(&expr->eval_scan.u.entity.notify_expr_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_node_init(&expr->eval_scan.u.entity.notify_expr_node);
++#endif /* SCAN_PARANOIA */
++ } break;
++ }
++
++ expr->eval_scan.seq = eval_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_expr: done: " PF_EXPR,
++ PA_EXPR(expr));
++}
++
++static void check_request_scan_membership TAC_ARGS((struct membership *membership, int flush));
++
++static void
++check_request_scan_membership(membership, flush)
++struct membership *membership;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_membership: " PF_MEMBERSHIP " (" PF_ERESULT_MEMBERSHIP ")",
++ PA_MEMBERSHIP(membership), PA_ERESULT_MEMBERSHIP(membership));
++#endif
++
++ if (!flush && membership->request_scan.seq == request_scan_seq)
++ return; /* up to date */
++
++#ifdef SCAN_PARANOIA
++ {
++ struct tac_list *virtual_list = tac_list_node_get_list(&membership->request_scan.virtual_membership_node);
++
++ if (virtual_list && virtual_list != &request_virtual_membership_list)
++ report(LOG_ERR, "Illegal list in membership->virtual_membership_node.list");
++ if (virtual_list)
++ tac_list_node_remove(&membership->request_scan.virtual_membership_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_node_init(&membership->request_scan.virtual_membership_node);
++#endif /* SCAN_PARANOIA */
++
++ membership->request_scan.seq = request_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_membership: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++/* we are cross-checking (membership<->parent entity)! */
++
++static void check_eval_scan_membership TAC_ARGS((struct membership *membership, int flush));
++
++static void
++check_eval_scan_membership(membership, flush)
++struct membership *membership;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++#endif
++
++ if (!flush && membership->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_request_scan_membership(membership, 0);
++
++ membership->eval_scan.unsolved = 1;
++ membership->eval_scan.seq = eval_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_membership: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++static void check_request_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_request_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_entity: " PF_ENTITY " (" PF_ERESULT_ENTITY ")",
++ PA_ENTITY(entity), PA_ERESULT_ENTITY(entity));
++#endif
++
++ if (!flush && entity->request_scan.seq == request_scan_seq)
++ return;
++
++ entity->request_scan.belongs = ER_UNKNOWN;
++ entity->request_scan.seq = request_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++static void check_value_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_value_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_value_scan_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++#endif
++
++ if (!flush && entity->value_scan.seq == value_scan_seq)
++ return;
++ check_request_scan_entity(entity, 0);
++
++ entity->value_scan.seen = 0;
++ entity->value_scan.from = NULL;
++ entity->value_scan.seq = value_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_value_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++static void check_eval_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_eval_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++ struct tac_list_node *child_membership_parent_node;
++
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++#endif
++
++ if (!flush && entity->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_value_scan_entity(entity, 0);
++
++ entity->eval_scan.unsolved_to_child_membership_num = entity->to_child_membership_num;
++
++ if ((child_membership_parent_node = tac_list_first_node(&entity->to_child_membership_list))) {
++ struct membership *child_membership = PARENT_NODE_TO_MEMBERSHIP(child_membership_parent_node);
++
++ entity->eval_scan.unsolved_to_child_membership_first = child_membership;
++ } else
++ entity->eval_scan.unsolved_to_child_membership_first = NULL;
++
++#ifdef SCAN_PARANOIA
++ {
++ struct tac_list_node *notify_expr_node;
++
++ while ((notify_expr_node = tac_list_first_node(&entity->eval_scan.notify_expr_list))) {
++ struct expr *notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node);
++
++ if (notify_expr->u.entity.entity != entity)
++ report(LOG_ERR, "INTERNAL: notify_expr->entity != entity");
++ if (notify_expr->eval_scan.seq != entity->eval_scan.seq)
++ report(LOG_ERR, "INTERNAL: notify_expr seq != entity seq");
++ tac_list_node_remove(notify_expr_node);
++ }
++
++ if (tac_list_node_get_list(&entity->eval_scan.pending_entity_node))
++ tac_list_node_remove(&entity->eval_scan.pending_entity_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_init(&entity->eval_scan.notify_expr_list);
++ tac_list_node_init(&entity->eval_scan.pending_entity_node);
++#endif /* SCAN_PARANOIA */
++
++ entity->eval_scan.seq = eval_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++
++/* invalidation managing section (for '*_scan_begin()'):
++ */
++
++/* this will happen once upon 'unsigned' overflow, ehm */
++
++#define INVALIDATE_SEQ_PTR(object,ptr) \
++ (G_STRUCT_MEMBER(unsigned, ptr, invalidate_scan_##object##_table[what]) = (unsigned) -1)
++#define INVALIDATE_SEQ(object) \
++ (INVALIDATE_SEQ_PTR(object,object))
++
++static const long invalidate_scan_expr_table[IS_MAX]={
++ G_STRUCT_OFFSET(struct expr, request_scan.seq),
++ -1,
++ G_STRUCT_OFFSET(struct expr, eval_scan.seq)};
++
++static void invalidate_scan_expr TAC_ARGS((struct expr *expr,enum invalidate_scan what));
++
++static void
++invalidate_scan_expr(expr_single, what)
++struct expr *expr_single;
++enum invalidate_scan what;
++{
++ struct expr *expr_parent, *expr_child;
++
++ if (!expr_single) {
++ report(LOG_ERR, "INTERNAL: NULL input expressions not support by invalidate_scan_expr");
++ return;
++ }
++ if (expr_single->parent) {
++ report(LOG_ERR, "INTERNAL: non-root expressions not supported by invalidate_scan_expr");
++ return;
++ }
++
++ /* TOP->DOWN scanner: */
++top_down:
++ do {
++ INVALIDATE_SEQ_PTR(expr,expr_single);
++ expr_parent = expr_single;
++
++ switch (expr_parent->type) {
++
++ case S_not:
++ expr_child = expr_parent->u.not.child;
++ continue;
++
++ case S_and:
++ case S_or:
++ expr_child = expr_parent->u.and_or.child_first;
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr_child = NULL; /* no child exists */
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal child node type %d for invalidate_scan_expr", expr_parent->type);
++ return;
++ }
++ } while ((expr_single = expr_child));
++ /* expr_child==NULL, we have only expr_parent: */
++
++ expr_child = expr_parent;
++
++ /* we have only expr_child: */
++ /* BOTTOM->UP scanner */
++ do {
++ if ((expr_single = expr_child->next))
++ goto top_down;
++ expr_parent = expr_child->parent;
++ } while ((expr_child = expr_parent));
++}
++
++static const long invalidate_scan_membership_table[IS_MAX]={
++ G_STRUCT_OFFSET(struct membership, request_scan.seq),
++ -1,
++ G_STRUCT_OFFSET(struct membership, eval_scan.seq)};
++
++static void invalidate_scan_membership TAC_ARGS((struct membership *membership,enum invalidate_scan what));
++
++static void
++invalidate_scan_membership(membership, what)
++struct membership *membership;
++enum invalidate_scan what;
++{
++ INVALIDATE_SEQ(membership);
++
++ if (membership->when)
++ invalidate_scan_expr(membership->when, what);
++}
++
++static const long invalidate_scan_entity_table[IS_MAX]={
++ G_STRUCT_OFFSET(ENTITY, request_scan.seq),
++ G_STRUCT_OFFSET(ENTITY, value_scan.seq),
++ G_STRUCT_OFFSET(ENTITY, eval_scan.seq)};
++
++static void invalidate_scan_entity TAC_ARGS((ENTITY *entity,enum invalidate_scan what));
++
++static void
++invalidate_scan_entity(entity, what)
++ENTITY *entity;
++enum invalidate_scan what;
++{
++ struct tac_list_node *child_membership_node;
++ struct membership *child_membership;
++
++ INVALIDATE_SEQ(entity);
++
++ if (what==IS_VALUE) /* optimalization */
++ return;
++
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_child_membership_list);
++ child_membership_node;
++ child_membership_node = tac_list_node_next(&child_membership->parent_node)
++ ) {
++ child_membership = PARENT_NODE_TO_MEMBERSHIP(child_membership_node);
++ invalidate_scan_membership(child_membership, what);
++ }
++}
++
++void scan_invalidate_entities_hashtable TAC_ARGS((void **hashtable, enum invalidate_scan what));
++
++void
++scan_invalidate_entities_hashtable(hashtable, what)
++void **hashtable;
++enum invalidate_scan what;
++{
++ int i;
++ ENTITY *entity;
++
++ for (i = 0; i < HASH_TAB_SIZE; i++)
++ for (entity = (ENTITY *) hashtable[i]; entity; entity = entity->hash)
++ invalidate_scan_entity(entity, what);
++}
++
++/* '*_scan_begin()' section:
++ */
++
++void request_scan_begin TAC_ARGS((void));
++
++void
++request_scan_begin()
++{
++#ifdef SCAN_PARANOIA
++ static int inited = 0;
++#endif /* SCAN_PARANOIA */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "request_scan_begin:");
++
++ request_scan_user_known = 0;
++
++ if (!++request_scan_seq)
++ scan_invalidate_entities(IS_REQUEST);
++
++#ifdef SCAN_PARANOIA
++ if (!inited) {
++#endif /* SCAN_PARANOIA */
++ tac_list_init(&request_virtual_membership_list);
++#ifdef SCAN_PARANOIA
++ inited = 1;
++ } else {
++ struct tac_list_node *virtual_membership_node;
++
++ while ((virtual_membership_node = tac_list_first_node(&request_virtual_membership_list))) {
++ struct membership *virtual_membership = VIRTUAL_MEMBERSHIP_NODE_TO_MEMBERSHIP(virtual_membership_node);
++
++ if (virtual_membership->request_scan.seq == request_scan_seq)
++ report(LOG_ERR, "INTERNAL: request_virtual_membership_list membership seq == ++request_scan_seq in request_scan_begin");
++ tac_list_node_remove(virtual_membership_node);
++ unlink_membership(virtual_membership);
++ free_membership(virtual_membership);
++ }
++ }
++#endif /* SCAN_PARANOIA */
++}
++
++static void value_scan_begin TAC_ARGS((ENTITY *entity));
++
++static void
++value_scan_begin(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_begin:");
++
++ if (!++value_scan_seq)
++ scan_invalidate_entities(IS_VALUE);
++
++ check_value_scan_entity(entity, 0); /* sure as seq invalidated */
++ /* assumed (entity->value_scan.from == NULL) */
++}
++
++#ifdef SCAN_PARANOIA
++
++static void eval_scan_begin_pending_entity_node TAC_ARGS((struct tac_list_node *pending_entity_node));
++
++static void
++eval_scan_begin_pending_entity_node(pending_entity_node)
++struct tac_list_node *pending_entity_node;
++{
++ ENTITY *pending_entity = PENDING_ENTITY_NODE_TO_ENTITY(pending_entity_node);
++
++ if (pending_entity->eval_scan.seq == eval_scan_seq)
++ report(LOG_ERR, "INTERNAL: eval_{pending}_entity_list entity seq == ++eval_scan_seq in eval_scan_begin");
++
++ tac_list_node_remove(pending_entity_node);
++}
++
++#endif /* SCAN_PARANOIA */
++
++static void eval_scan_begin TAC_ARGS((void));
++
++static void
++eval_scan_begin()
++{
++#ifdef SCAN_PARANOIA
++ static int inited = 0;
++#endif /* SCAN_PARANOIA */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "eval_scan_begin:");
++
++ if (!++eval_scan_seq)
++ scan_invalidate_entities(IS_EVAL);
++
++#ifdef SCAN_PARANOIA
++ if (!inited) {
++#endif /* SCAN_PARANOIA */
++ tac_list_init(&eval_kicked_entity_list);
++ tac_list_init(&eval_destroy_entity_list);
++ tac_list_init(&eval_notified_expr_list);
++#ifdef SCAN_PARANOIA
++ inited = 1;
++ } else {
++ struct tac_list_node *pending_entity_node;
++ struct tac_list_node *notify_expr_node;
++
++ while ((pending_entity_node = tac_list_first_node(&eval_kicked_entity_list)))
++ eval_scan_begin_pending_entity_node(pending_entity_node);
++ while ((pending_entity_node = tac_list_first_node(&eval_destroy_entity_list)))
++ eval_scan_begin_pending_entity_node(pending_entity_node);
++
++ while ((notify_expr_node = tac_list_first_node(&eval_notified_expr_list))) {
++ struct expr *notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node);
++
++ if (notify_expr->eval_scan.seq == eval_scan_seq)
++ report(LOG_ERR, "INTERNAL: eval_notified_expr_list expr seq == ++eval_scan_seq in eval_scan_begin");
++
++ tac_list_node_remove(notify_expr_node);
++ }
++ }
++#endif /* SCAN_PARANOIA */
++}
++
++/* 'priority=0' => addtail - used for WANTED entities
++ * 'priority=1' => addhead - used for SOLVED entities
++ * It may be better to do insert it AFTER all currently solved
++ * entities but may be not but who cares...
++ */
++
++static void register_kicked_entity TAC_ARGS((ENTITY *entity, int priority));
++
++static void register_kicked_entity(entity, priority)
++ENTITY *entity;
++int priority;
++{
++ struct tac_list_node *pending_entity_node = &entity->eval_scan.pending_entity_node;
++
++ check_eval_scan_entity(entity, 0);
++
++ if (tac_list_node_get_list(pending_entity_node) == &eval_destroy_entity_list) {
++ tac_list_node_remove (pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "register_kicked_entity: REMOVED " PF_ENTITY " from eval_DESTROY_entity_list",
++ PA_ENTITY(entity));
++ }
++ if (tac_list_node_get_list(pending_entity_node) == NULL) {
++ if (priority)
++ tac_list_addhead(&eval_kicked_entity_list, pending_entity_node);
++ else
++ tac_list_addtail(&eval_kicked_entity_list, pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "register_kicked_entity: REGISTERED " PF_ENTITY " to eval_KICKED_entity_list (priority=%s)",
++ PA_ENTITY(entity), (priority ? "YES" : "NO"));
++ }
++#ifdef SCAN_PARANOIA
++ if ((tac_list_node_get_list(pending_entity_node) != &eval_kicked_entity_list)) {
++ report(LOG_ERR, "Illegal list in entity->pending_entity_node.list");
++ return;
++ }
++#endif
++}
++
++/* check_eval_scan_*() is assumed both for "expr" and for "entity" ! */
++
++static void expr_eval_notify_expr TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr(expr)
++struct expr *expr;
++{
++ ENTITY *entity = expr->u.entity.entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REGISTERED notify " PF_EXPR " when " PF_ENTITY " is known",
++ PA_EXPR(expr), PA_ENTITY(entity));
++
++ if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node)) {
++#ifdef SCAN_PARANOIA
++ if (&entity->eval_scan.notify_expr_list
++ != tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node))
++ report(LOG_ERR, "Another " PF_ENTITY " already registered in notify node of " PF_EXPR,
++ PA_ENTITY(EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr)), PA_EXPR(expr));
++#endif
++ return;
++ }
++
++ tac_list_addtail(&entity->eval_scan.notify_expr_list,
++ &expr->eval_scan.u.entity.notify_expr_node);
++
++ register_kicked_entity(entity, 0 /* priority */);
++}
++
++/* check_eval_scan_*() is assumed for "expr" ! */
++
++static void expr_eval_notify_expr_remove_internal TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr_remove_internal(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_remove_internal: no longer interested in " PF_EXPR,
++ PA_EXPR(expr));
++
++ if (!expr)
++ return;
++ if (expr->eval_scan.seq != eval_scan_seq)
++ return;
++ if (expr->request_scan.result != ER_UNKNOWN)
++ return;
++
++ switch (expr->type) {
++
++ case S_not:
++ expr_eval_notify_expr_remove_internal(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or: {
++ struct expr *child;
++
++ for (child=expr->u.and_or.child_first; child; child=child->next)
++ expr_eval_notify_expr_remove_internal(child);
++ } break;
++
++ case S_user:
++ case S_host:
++ case S_group: {
++ ENTITY *entity = expr->u.entity.entity;
++ struct tac_list_node *pending_entity_node = &entity->eval_scan.pending_entity_node;
++
++ if (!tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node))
++ break;
++
++ tac_list_node_remove(&expr->eval_scan.u.entity.notify_expr_node);
++ if (tac_list_first_node(&entity->eval_scan.notify_expr_list))
++ break;
++ /* no one is further interested in "entity" */
++
++ if ((tac_list_node_get_list(pending_entity_node) == &eval_kicked_entity_list)) {
++ tac_list_node_remove (pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REMOVED " PF_ENTITY " from eval_KICKED_entity_list",
++ PA_ENTITY(entity));
++ }
++ if (tac_list_node_get_list(pending_entity_node) == NULL) {
++ tac_list_addtail(&eval_destroy_entity_list, pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REGISTERED " PF_ENTITY " to eval_DESTROY_entity_list",
++ PA_ENTITY(entity));
++ }
++#ifdef SCAN_PARANOIA
++ if (tac_list_node_get_list(pending_entity_node) != &eval_destroy_entity_list) {
++ report(LOG_ERR, "Illegal list in entity->pending_entity_node.list");
++ return;
++ }
++#endif
++
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for expr_eval_notify_expr_remove", expr->type);
++ return;
++ }
++}
++
++static void expr_eval_notify_expr_flush_destroy_entity_list TAC_ARGS((void));
++
++static void expr_eval_notify_expr_flush_destroy_entity_list()
++{
++struct tac_list_node *destroy_entity_node;
++
++ while ((destroy_entity_node = tac_list_first_node(&eval_destroy_entity_list))) {
++ ENTITY *destroy_entity = PENDING_ENTITY_NODE_TO_ENTITY(destroy_entity_node);
++ struct tac_list_node *destroy_notify_expr_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_flush_destroy_entity_list: PROCESSING " PF_ENTITY " from eval_DESTROY_entity_list",
++ PA_ENTITY(destroy_entity));
++
++ while ((destroy_notify_expr_node = tac_list_first_node(&destroy_entity->eval_scan.notify_expr_list))) {
++ struct expr *destroy_notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(destroy_notify_expr_node);
++
++ expr_eval_notify_expr_remove_internal(destroy_notify_expr);
++ }
++ }
++}
++
++static void expr_eval_notify_expr_remove TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr_remove(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_remove: no longer interested in " PF_EXPR,
++ PA_EXPR(expr));
++
++ expr_eval_notify_expr_remove_internal(expr);
++ expr_eval_notify_expr_flush_destroy_entity_list();
++}
++
++/* It would be very nice to try to optimize the expression before evaluation.
++
++ We are not interested in some CPU time complexity of the expression.
++ But we would be very happy to discard any user/host/group membership
++ dependencies (our 'variables'). Unfortunately such optimization is
++ NP problem (classification by courtesy of Daniel Kral) so it is considered
++ too expensive for us.
++
++ TODO in future: Full NP optimization for small number of variables and/or
++ heuristic optimizations for complex expressions.
++*/
++
++static enum eval_result expr_eval_immediate TAC_ARGS((struct expr *expr_single));
++
++static enum eval_result
++expr_eval_immediate(expr_single)
++struct expr *expr_single;
++{
++ struct expr *expr_child, *expr_parent;
++ enum eval_result result_child, result_parent = 0 /* GCC paranoia */;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ if (!expr_single)
++ return (ER_TRUE);
++
++ /* TOP->DOWN scanner: */
++top_down:
++ while (1) {
++ enum eval_result result_single;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: top_down start: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ check_eval_scan_expr(expr_single, 0);
++ result_single = expr_single->request_scan.result;
++ if (result_single != ER_UNKNOWN)
++ break;
++ switch (expr_single->type) {
++
++ case S_not:
++ expr_single = expr_single->u.not.child;
++ continue;
++
++ case S_and:
++ case S_or:
++ expr_single = expr_single->u.and_or.child_first;
++ continue;
++
++ case S_user:
++ case S_host:
++ case S_group: {
++ ENTITY *entity = expr_single->u.entity.entity;
++
++ check_eval_scan_entity(entity, 0);
++
++ if (entity->request_scan.belongs == ER_UNKNOWN)
++ expr_eval_notify_expr(expr_single);
++ else
++ result_single = entity->request_scan.belongs;
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal child node type %d for expr_eval", expr_single->type);
++ return (ER_UNKNOWN);
++ }
++
++ expr_single->request_scan.result = result_single;
++ break;
++ }
++
++ /* BOTTOM->UP scanner: */
++ do {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up start: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ expr_parent = expr_single->parent;
++ if (!expr_parent)
++ break;
++ if (expr_parent->eval_scan.seq != eval_scan_seq) {
++ report(LOG_ERR, "INTERNAL: Parent expr node eval_scan NOT up-to-date");
++ return (ER_UNKNOWN);
++ }
++ if (expr_parent->request_scan.seq != request_scan_seq) {
++ report(LOG_ERR, "INTERNAL: Parent expr node request_scan NOT up-to-date");
++ return (ER_UNKNOWN);
++ }
++ if (expr_parent->request_scan.result != ER_UNKNOWN) {
++ report(LOG_WARNING, "INTERNAL-WARNING: Parent expr node already known, wasteful eval occured");
++ return (ER_UNKNOWN);
++ }
++
++ expr_child = expr_single;
++ result_child = expr_child->request_scan.result;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up switch: child=" PF_EXPR ",parent=" PF_EXPR,
++ PA_EXPR(expr_child), PA_EXPR(expr_parent));
++
++ switch (expr_parent->type) {
++
++ case S_not:
++ switch (result_child) {
++ case ER_UNKNOWN: result_parent = ER_UNKNOWN; break;
++ case ER_FALSE: result_parent = ER_TRUE; break;
++ case ER_TRUE: result_parent = ER_FALSE; break;
++ }
++ break;
++
++ case S_and:
++ case S_or: {
++ enum eval_result veto = (expr_parent->type==S_and ? ER_FALSE : ER_TRUE );
++ enum eval_result consent = (expr_parent->type==S_and ? ER_TRUE : ER_FALSE);
++
++ if (result_child == veto)
++ result_parent = veto;
++ else if (result_child == ER_UNKNOWN || result_child == consent) {
++ if (expr_child->next) {
++ expr_single = expr_child->next;
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up and_or: traversed to and_or next: " PF_EXPR,
++ PA_EXPR(expr_single));
++ goto top_down;
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up and_or: full scan: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ /* It would be nice to pretend that all 'veto' decisions already made in the child
++ * had to set our 'result' to the correct value. But 'consent' decisions don't set
++ * us and the behaviour of auto-set from the child in 'veto' case may get changed
++ * in the future versions.
++ * So we rather don't depend on it.
++ * This overhead doesn't change altgorithmic complexity anyway.
++ */
++ result_parent = consent;
++ for (expr_child = expr_parent->u.and_or.child_first; expr_child; expr_child = expr_child->next)
++ {
++ check_eval_scan_expr(expr_child, 0); /* shouldn't be needed */
++ if (expr_child->request_scan.result == ER_UNKNOWN)
++ result_parent = ER_UNKNOWN; /* assumed (result_parent != veto) */
++ else if (expr_child->request_scan.result == veto) {
++ result_parent = veto;
++ break;
++ }
++ }
++ break;
++ }
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal parent node type %d for expr_eval", expr_parent->type);
++ return (ER_UNKNOWN);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up end: child=" PF_EXPR ",parent=" PF_EXPR,
++ PA_EXPR(expr_child), PA_EXPR(expr_parent));
++
++ if (result_parent != ER_UNKNOWN) {
++ expr_parent->request_scan.result = result_parent;
++ /* we no longer need any notifications from entities to solve sub-expression */
++ expr_eval_notify_expr_remove(expr_parent);
++ }
++
++ expr_single = expr_parent;
++ } while (0);
++ /* The whole expression has been scanned to its root, we have "expr_single" */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: done: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ return (expr_single->request_scan.result);
++}
++
++static void membership_solved TAC_ARGS((struct membership *membership));
++
++static void
++membership_solved(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ check_request_scan_entity(parent_entity, 0);
++
++#ifdef SCAN_PARANOIA
++ if (!membership->eval_scan.unsolved) {
++ report(LOG_ERR, "INTERNAL: membership already solved in membership_solved");
++ return;
++ }
++#endif
++
++ membership->eval_scan.unsolved = 0;
++
++#ifdef SCAN_PARANOIA
++ if (!parent_entity->eval_scan.unsolved_to_child_membership_num) {
++ report(LOG_ERR, "INTERNAL: unsolved_to_child_membership_num-- == 0 in membership_solved");
++ parent_entity->eval_scan.unsolved_to_child_membership_num++;
++ }
++#endif
++ parent_entity->eval_scan.unsolved_to_child_membership_num--;
++
++ if (!parent_entity->eval_scan.unsolved_to_child_membership_num
++ && parent_entity->request_scan.belongs == ER_UNKNOWN) {
++ parent_entity->request_scan.belongs = ER_FALSE;
++ register_kicked_entity(parent_entity, 1 /* priority */);
++ }
++}
++
++static void membership_parent_solve TAC_ARGS((struct membership *membership, enum eval_result how));
++
++static void
++membership_parent_solve(membership, how)
++struct membership *membership;
++enum eval_result how;
++{
++ enum eval_result negative = (how == ER_TRUE ? ER_FALSE : ER_TRUE);
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ check_request_scan_entity(parent_entity, 0);
++
++ if (parent_entity->request_scan.belongs == negative)
++ report(LOG_ERR, "INTERNAL: parent " PF_ENTITY "already negative to what says membership " PF_MEMBERSHIP "in membership_eval_immediate",
++ PA_ENTITY(parent_entity), PA_MEMBERSHIP(membership));
++
++ parent_entity->request_scan.belongs = how;
++ register_kicked_entity(parent_entity, 1 /* priority */);
++
++ membership_solved(membership);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_parent_solve: " PF_MEMBERSHIP " marked parent " PF_ENTITY,
++ PA_MEMBERSHIP(membership), PA_ENTITY(parent_entity));
++}
++
++static void membership_eval_immediate TAC_ARGS((struct membership *membership));
++
++static void
++membership_eval_immediate(membership)
++struct membership *membership;
++{
++ enum eval_result membership_valid;
++ ENTITY *child_entity, *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_eval_immediate: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ check_eval_scan_membership(membership, 0);
++
++ if (!membership->eval_scan.unsolved)
++ return;
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++ check_request_scan_entity(parent_entity, 0);
++ if (parent_entity->request_scan.belongs != ER_UNKNOWN) /* why to solve this membership? */
++ return;
++
++ membership_valid = expr_eval_immediate(membership->when);
++
++ child_entity = MEMBERSHIP_TO_CHILD_ENTITY( membership);
++ check_request_scan_entity( child_entity, 0);
++
++#if 0 /* non-valid membership doesn't YET solve the parent! */
++ if (child_entity->request_scan.belongs == ER_FALSE || membership_valid == ER_FALSE) {
++ membership_parent_solve(membership, ER_FALSE);
++ return;
++ }
++#endif
++
++ if (child_entity->request_scan.belongs == ER_TRUE && membership_valid == ER_TRUE ) {
++ membership_parent_solve(membership, ER_TRUE );
++ return;
++ }
++
++ if ( child_entity->request_scan.belongs == ER_UNKNOWN)
++ register_kicked_entity( child_entity, 0 /* priority */);
++ if (parent_entity->request_scan.belongs == ER_UNKNOWN)
++ register_kicked_entity(parent_entity, 0 /* priority */);
++
++ if (parent_entity->request_scan.belongs != ER_UNKNOWN
++ || (child_entity->request_scan.belongs == ER_FALSE || membership_valid == ER_FALSE))
++ membership_solved(membership);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_eval_immediate: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++static void entity_eval_immediate TAC_ARGS((ENTITY *entity));
++
++static void
++entity_eval_immediate(entity)
++ENTITY *entity;
++{
++ struct tac_list_node *notified_expr_node;
++ struct tac_list_node *child_membership_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ check_eval_scan_entity(entity, 0);
++
++ if (!request_scan_user_known) {
++#ifdef SCAN_PARANOIA
++ if (entity->request_scan.belongs != ER_UNKNOWN)
++ report(LOG_ERR, "INTERNAL: belonging known while still !request_scan_user_known for " PF_ENTITY " in entity_eval_immediate",
++ PA_ENTITY(entity));
++#endif
++ return;
++ }
++
++ if (entity->request_scan.belongs == ER_UNKNOWN) {
++ if (entity->eval_scan.unsolved_to_child_membership_first) {
++ struct membership *order_membership = entity->eval_scan.unsolved_to_child_membership_first;
++ struct tac_list_node *next_membership_parent_node = tac_list_node_next(&order_membership->parent_node);
++
++ membership_eval_immediate(order_membership);
++ if (next_membership_parent_node)
++ entity->eval_scan.unsolved_to_child_membership_first = PARENT_NODE_TO_MEMBERSHIP(next_membership_parent_node);
++ else
++ entity->eval_scan.unsolved_to_child_membership_first = NULL;
++
++ register_kicked_entity(entity, 0 /* priority */);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: finishing as we ordered child membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(order_membership));
++ return;
++ }
++
++ if (!entity->eval_scan.unsolved_to_child_membership_num)
++ entity->request_scan.belongs = ER_FALSE;
++ else {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: finishing as unsolved child memberships still available and I'm still clueless");
++ return;
++ }
++ }
++ /* belonging is known here */
++
++ /* recheck all memberships we may decide */
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ child_membership_node;
++ child_membership_node = tac_list_node_next(child_membership_node)
++ ) {
++ struct membership *child_membership = CHILD_NODE_TO_MEMBERSHIP(child_membership_node);
++
++ membership_eval_immediate(child_membership);
++ }
++
++ /* notify all exprs which are interested in us */
++ while ((notified_expr_node = tac_list_first_node(&entity->eval_scan.notify_expr_list))) {
++ tac_list_node_remove(notified_expr_node);
++ tac_list_addtail(&eval_notified_expr_list, notified_expr_node);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++
++enum eval_result expr_eval TAC_ARGS((struct expr *expr));
++
++enum eval_result
++expr_eval(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: top level order for " PF_EXPR,
++ PA_EXPR(expr));
++
++ if (!expr)
++ return (ER_TRUE);
++
++ eval_scan_begin();
++ if (expr_eval_immediate(expr) != ER_UNKNOWN)
++ return (expr->request_scan.result);
++
++ /* all 'solved' nodes MUST be removed BEFORE '*_immediate()' has been called,
++ * otherwise we may have no longer valid node!
++ */
++ for (;;) {
++ struct tac_list_node *notified_expr_node, *kicked_entity_node;
++
++ /* check it rather always, checking just on notifications looks too complex.
++ */
++ if (expr->request_scan.result != ER_UNKNOWN) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: finishing as ordered " PF_EXPR " got known",
++ PA_EXPR(expr));
++ return (expr->request_scan.result);
++ }
++
++#if 0 /* not needed as it is now always called after any destroy */
++ expr_eval_notify_expr_flush_destroy_entity_list(); /* eval_destroy_entity_list */
++#endif /* not needed */
++
++ if ((notified_expr_node = tac_list_first_node(&eval_notified_expr_list))) {
++ struct expr *notified_expr = NOTIFY_EXPR_NODE_TO_EXPR(notified_expr_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: PROCESSING " PF_EXPR " from eval_NOTIFIED_expr_list",
++ PA_EXPR(notified_expr));
++
++ tac_list_node_remove(notified_expr_node);
++ expr_eval_immediate(notified_expr);
++
++ if (notified_expr->membership)
++ membership_eval_immediate(notified_expr->membership);
++
++ continue; /* shortcut */
++ }
++
++ if ((kicked_entity_node = tac_list_first_node(&eval_kicked_entity_list))) {
++ ENTITY *kicked_entity = PENDING_ENTITY_NODE_TO_ENTITY(kicked_entity_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: PROCESSING " PF_ENTITY " from eval_KICKED_entity_list",
++ PA_ENTITY(kicked_entity));
++
++ tac_list_node_remove(kicked_entity_node);
++ entity_eval_immediate(kicked_entity);
++ continue; /* shortcut */
++ }
++
++ break; /* nothing done yet, all lists are empty! */
++ }
++
++ if (!expr->request_scan.loop_reported) {
++ report(LOG_WARNING, "Unable to resolve expression from line %d, some looping occured", expr->line);
++ expr->request_scan.loop_reported = 1;
++ }
++ return (ER_UNKNOWN);
++}
++
++
++void eval_force_belong_entity TAC_ARGS((ENTITY *entity));
++
++void eval_force_belong_entity(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "eval_force_belong_entity: " PF_ENTITY " (before check_scan " PF_ERESULT_ENTITY ")",
++ PA_ENTITY(entity), PA_ERESULT_ENTITY(entity));
++
++ check_request_scan_entity(entity, 0);
++
++ if (entity->request_scan.belongs == ER_FALSE)
++ report(LOG_ERR, "Dangerous force of TRUE to FALSE-determined entity in eval_force_belong_entity");
++
++ entity->request_scan.belongs = ER_TRUE;
++}
++
++void scan_init_entity TAC_ARGS((ENTITY *entity));
++
++void
++scan_init_entity(entity)
++ENTITY *entity;
++{
++ entity->request_scan.seq = request_scan_seq-1; /* invalidate */
++ entity-> value_scan.seq = value_scan_seq-1; /* invalidate */
++ entity-> eval_scan.seq = eval_scan_seq-1; /* invalidate */
++ tac_list_init(&entity->eval_scan.notify_expr_list);
++ tac_list_node_init(&entity->eval_scan.pending_entity_node);
++}
++
++struct membership *enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child, struct expr *when));
++
++struct membership *
++enlist_entity_direct(parent, child, when)
++ENTITY *parent;
++ENTITY *child;
++struct expr *when;
++{
++ struct membership *membership = (struct membership *) tac_malloc(sizeof(struct membership));
++
++ tac_list_node_init(&membership->parent_node);
++ tac_list_node_init(&membership->child_node);
++ membership->request_scan.seq = request_scan_seq-1;
++ tac_list_node_init(&membership->request_scan.virtual_membership_node);
++ membership->eval_scan.seq = eval_scan_seq-1;
++
++ tac_list_addtail(&parent->to_child_membership_list , &membership->parent_node);
++ parent->to_child_membership_num++;
++ tac_list_addtail(& child->to_parent_membership_list, &membership-> child_node);
++ membership->when = when;
++ if (expr_sink(membership->when, membership)) {
++ unlink_membership(membership);
++ free_membership(membership);
++ return (NULL);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "enlist_entity_direct: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ return (membership);
++}
++
++struct membership *virtual_enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child));
++
++struct membership *
++virtual_enlist_entity_direct(parent, child)
++ENTITY *parent;
++ENTITY *child;
++{
++ struct membership *membership;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "virtual_enlist_entity_direct: the following enlist will be VIRTUAL...");
++
++ membership = enlist_entity_direct(parent, child, NULL /* when */);
++ if (!membership)
++ return (NULL);
++
++ check_request_scan_membership(membership, 0);
++ tac_list_addtail(&request_virtual_membership_list, &membership->request_scan.virtual_membership_node);
++
++ return (membership);
++}
++
++/* returns given 'entity' or NULL if already visited */
++
++void (*value_scan_forward_seen_hook) TAC_ARGS((struct membership *membership));
++
++static ENTITY *value_scan_forward TAC_ARGS((struct membership *membership));
++
++static ENTITY *
++value_scan_forward(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: from " PF_MEMBERSHIP " try forward...",
++ PA_MEMBERSHIP(membership));
++
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ if (ER_TRUE != expr_eval(membership->when)) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward NOT successful due to failed 'when' evaluation.");
++ return (NULL);
++ }
++ check_value_scan_entity(parent_entity, 0);
++ if (parent_entity->value_scan.seen) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward NOT successful as the parent " PF_ENTITY " was already seen this value scan.",
++ PA_ENTITY(parent_entity));
++ if (value_scan_forward_seen_hook)
++ (*value_scan_forward_seen_hook)(membership);
++ return (NULL);
++ }
++ parent_entity->value_scan.seen = 1;
++ parent_entity->value_scan.from = membership;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward SUCCESSFUL to parent " PF_ENTITY,
++ PA_ENTITY(parent_entity));
++ return (parent_entity);
++}
++
++struct membership *value_scan_backward TAC_ARGS((ENTITY *entity));
++
++struct membership *
++value_scan_backward(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_backward: from " PF_ENTITY " went back to " PF_MEMBERSHIP,
++ PA_ENTITY(entity), PA_MEMBERSHIP(entity->value_scan.from));
++
++#ifdef SCAN_PARANOIA
++ if (entity->value_scan.seq != value_scan_seq) {
++ report(LOG_ERR, "entity value_scan NOT up-to-date in value_scan_backward");
++ return (NULL);
++ }
++#endif
++
++ return (entity->value_scan.from);
++}
++
++/* Scan the entity graph and return each node found.
++ 'when' conditions for graph connections are respected,
++ looping is correctly prevented.
++*/
++
++enum value_scan_func_result value_scan_entity TAC_ARGS((ENTITY *entity, int recurse, value_scan_func_t func, void *func_data));
++
++enum value_scan_func_result
++value_scan_entity(entity, recurse, func, func_data)
++ENTITY *entity;
++int recurse;
++value_scan_func_t func;
++void *func_data;
++{
++ enum value_scan_func_result vsfr;
++ struct tac_list_node *membership_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: " PF_ENTITY ", recurse=%d",
++ PA_ENTITY(entity), recurse);
++
++ vsfr=(*func)(entity,func_data);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: root func-> " PF_VSFR,
++ PA_VSFR(vsfr));
++
++ if (vsfr != VSFR_CONTINUE) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as root func didn't return VSFR_CONTINUE");
++ return (vsfr);
++ }
++ if (!recurse ) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as recurse not ordered");
++ return (VSFR_STOP);
++ }
++
++ value_scan_begin(entity);
++ membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ if (!membership_node) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as no parent entities of root");
++ return (VSFR_CONTINUE); /* no parent entities */
++ }
++
++ while (1) {
++ struct membership *membership = CHILD_NODE_TO_MEMBERSHIP(membership_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: trace loop start: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ entity = value_scan_forward(membership);
++ if (entity) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: successful recurse to " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ vsfr=(*func)(entity,func_data);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: func(" PF_ENTITY ")-> " PF_VSFR,
++ PA_ENTITY(entity), PA_VSFR(vsfr));
++
++ if (vsfr == VSFR_FOUND) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as func returned VSFR_FOUND");
++ return (vsfr);
++ }
++ if (vsfr == VSFR_CONTINUE)
++ membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ }
++ if (!entity || vsfr == VSFR_STOP) {
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership); /* for retreat from the LAST membership */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: unsuccessful recurse to " PF_ENTITY ", tracing back through child " PF_ENTITY,
++ PA_ENTITY(parent_entity), PA_ENTITY(entity));
++
++ membership_node = tac_list_node_next(&membership->child_node);
++ }
++
++ while (!membership_node) {
++ membership = value_scan_backward(entity);
++ if (!membership) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as all nodes were scanned");
++ return (VSFR_CONTINUE); /* FINISH */
++ }
++
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership); /* for retreat from the LAST membership */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: backward retreat ('next' chase) "
++ "through " PF_MEMBERSHIP " to child " PF_ENTITY,
++ PA_MEMBERSHIP(membership), PA_ENTITY(entity));
++
++ membership_node = tac_list_node_next(&membership->child_node);
++ }
++ }
++ /* NOTREACHED */
++}
+diff --git a/cfgeval.h b/cfgeval.h
+new file mode 100644
+index 0000000..63f33cb
+--- /dev/null
++++ b/cfgeval.h
+@@ -0,0 +1,134 @@
++#ifndef CFGEVAL_H
++#define CFGEVAL_H 1
++
++#include "tac_plus.h"
++
++#include "utils.h"
++
++
++struct entity;
++typedef struct entity ENTITY;
++enum invalidate_scan {
++ IS_REQUEST = 0,
++ IS_VALUE = 1,
++ IS_EVAL = 2,
++ IS_MAX = 3
++};
++
++enum eval_result {
++ ER_UNKNOWN,
++ ER_FALSE,
++ ER_TRUE
++};
++
++enum value_scan_func_result {
++ VSFR_CONTINUE, /* for value_scan_entity() it means 'not found' */
++ VSFR_FOUND, /* immediately return from the whole value_scan_entity() */
++ VSFR_STOP /* backtrack (stop) this branch, scan further; nevere returned by value_scan_entity() */
++};
++
++struct membership {
++ struct tac_list_node parent_node; /* to_child_membership_list , AKA legacy "member" */
++ struct tac_list_node child_node; /* to_parent_membership_list */
++
++ struct {
++ unsigned seq; /* corresponds to global request_scan_seq */
++ struct tac_list_node virtual_membership_node;
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ unsigned unsolved:1; /* we haven't yet decreased
++ * parent_entity->eval_scan.unsolved_to_child_membership_num */
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++
++ struct expr *when;
++};
++#define MEMBERSHIP_TO_PARENT_ENTITY(membership) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(membership)->parent_node), to_child_membership_list ))
++#define MEMBERSHIP_TO_CHILD_ENTITY(membership) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(membership)-> child_node), to_parent_membership_list))
++#define PARENT_NODE_TO_MEMBERSHIP(parent_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (parent_node_), parent_node))
++#define CHILD_NODE_TO_MEMBERSHIP( child_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, ( child_node_), child_node))
++#define UNSOLVED_CHILD_NODE_TO_MEMBERSHIP(unsolved_child_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (unsolved_child_node_), eval_scan.unsolved_child_node))
++#define VIRTUAL_MEMBERSHIP_NODE_TO_MEMBERSHIP(virtual_membership_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (virtual_membership_node_), request_scan.virtual_membership_node))
++
++struct expr {
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ enum eval_result result; /* known result of this whole branch */
++ unsigned loop_reported:1; /* prevent excessive logging */
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ union {
++
++ struct { /* for S_host, S_user or S_group */
++ /* connected to either "entity->eval_scan.notify_expr_list"
++ * or to global "eval_notified_expr_list"
++ */
++ struct tac_list_node notify_expr_node; /* gets removed on this expr->seq!= */
++ } entity;
++
++ } u;
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++
++ struct membership *membership; /* our owner */
++ struct expr *parent; /* NULL if we are the root expr */
++ /* "parent" also (mis)used by expr_sink_{register,commit}() ! */
++ struct expr *next; /* used in childs of S_and / S_or */
++ int type; /* S_not, S_and, S_or, S_host, S_user or S_group */
++ int line;
++ union {
++
++ struct {
++ struct expr *child;
++ } not;
++
++ struct {
++ struct expr *child_first; /* linked by expr->next */
++ } and_or;
++
++ struct { /* for S_host, S_user or S_group */
++ const char *name;
++ ENTITY *entity;
++ } entity;
++
++ } u;
++};
++#define EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr_) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(expr_)->eval_scan.u.entity.notify_expr_node), eval_scan.notify_expr_list))
++#define NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node_) \
++ (&TAC_MEMBER_STRUCT(struct expr, (notify_expr_node_), eval_scan.u.entity.notify_expr_node))
++
++typedef enum value_scan_func_result (*value_scan_func_t) TAC_ARGS((ENTITY *entity,void *func_data));
++extern void (*value_scan_forward_seen_hook) TAC_ARGS((struct membership *membership));
++
++
++extern int request_scan_user_known; /* have we allowed to 'solve' S_user entities at all? */
++
++
++extern void unlink_expr TAC_ARGS((struct expr *expr));
++extern void free_expr TAC_ARGS((struct expr *expr));
++extern void scan_free_entity TAC_ARGS((ENTITY *entity));
++extern struct expr *new_expr TAC_ARGS((int type));
++extern enum value_scan_func_result value_scan_entity TAC_ARGS((ENTITY *entity, int recurse, value_scan_func_t func, void *func_data));
++extern struct membership *value_scan_backward TAC_ARGS((ENTITY *entity));
++extern enum eval_result expr_eval TAC_ARGS((struct expr *expr_single));
++extern struct expr *dupl_expr TAC_ARGS((const struct expr *in));
++extern void scan_init_entity TAC_ARGS((ENTITY *entity));
++extern struct membership *enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child, struct expr *when));
++extern struct membership *virtual_enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child));
++extern void scan_invalidate_entities_hashtable TAC_ARGS((void **hashtable, enum invalidate_scan what));
++extern void request_scan_begin TAC_ARGS((void));
++extern void eval_force_belong_entity TAC_ARGS((ENTITY *entity));
++extern void expr_sink_register TAC_ARGS((struct expr *expr));
++extern int expr_sink_commit TAC_ARGS((void));
++
++
++#endif /* CFGEVAL_H */
+diff --git a/cfgfile.c b/cfgfile.c
+index 6eaef53..041993c 100644
+--- a/cfgfile.c
++++ b/cfgfile.c
+@@ -17,38 +17,87 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
+ #include
++#include
+ #include
+-#include "regexp.h"
++#include
++
++#ifndef WITH_INCLUDED_REGEX
++#ifdef HAVE_REGEX_H
++#include
++#endif
++#endif
++
++#include "cfgfile.h"
++#include "report.h"
++#include "utils.h"
++#include "hash.h"
++#include "parse.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++
++#ifdef WITH_INCLUDED_REGEX
++#include "tac_regexp.h"
++#endif
++
++
++static void sym_get TAC_ARGS((void));
++static void when_expr_root_init TAC_ARGS((void));
++static void rch TAC_ARGS((void));
++static int parse_entity TAC_ARGS((int entity_type));
++static NODE *parse_svcs TAC_ARGS((void));
++static int parse_conditional_block TAC_ARGS((ENTITY *entity));
++static NODE *parse_cmd_matches TAC_ARGS((void));
++static NODE *parse_attrs TAC_ARGS((void));
++static void getsym TAC_ARGS((void));
++static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity));
++
+
+ /*
+ := *
+
+- := |
++ := |
+
+ := |
+- accounting file =
++ accounting file = |
+ default authorization = permit |
+- key =
++ key = |
++ authorization = ( first | recursive )
+
+ := default authentication = file
+ #if defined(DB)
+- | db )
++ | db
+ #endif
+
+- := permit | deny
++ := permit | deny
+
+ :=
+
+ :=
+
+ := user = {
+- [ default service = [ permit | deny ] ]
++ [ ]
+ *
+ *
+ }
+
++ := host = {
++ [ ]
++ *
++ *
++ }
++
++ := group = {
++ [ ]
++ *
++ *
++ }
++
++ := default service = ( permit | deny | default )
++
+ := file |
+ skey |
+ cleartext |
+@@ -80,23 +129,66 @@
+ global = cleartext |
+ msg =
+ before authorization = |
+- after authorization =
++ after authorization = |
++
++
++ := key = |
++
++
++ := enlist = |
++ key = |
++
++
++ := member = |
++ enlist = |
++ |
++
++
++ := {
++ *
++ }
+
+ := |
+
+ := cmd = {
+- *
++ *
+ }
+
+- :=
++ := |
++
++
++ := {
++ *
++ }
+
+- := service = ( exec | arap | slip | ppp protocol = {
++ := service = ( exec | arap | slip | ppp protocol = ) {
++# first matching is the FINAL one, no further graph scanning occurs!
+ [ default attribute = permit ]
+- *
++ *
++ }
++
++ := [ optional ] = |
++
++
++ := {
++ *
+ }
+
+- := [ optional ] =
++ := when =
+
++# to avoid ambiguous precence by forbid of "or" & "and" without parentheses:
++ := |
++ not |
++ '(' ')' |
++ '(' ')'
++
++ := |
++ or
++
++ := |
++ and
++
++ := ( user | host | group )
+ */
+
+ static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */
+@@ -107,102 +199,54 @@ static int sym_line = 1; /* current line number for parsing */
+ static FILE *cf = NULL; /* config file pointer */
+ static int sym_error = 0; /* a parsing error has occurred */
+ static int no_user_dflt = 0; /* default if user doesn't exist */
++ /* ='default authorization': 0/S_permit */
++static int algorithm_recursive = 0; /* use newer authorization alogrithm? */
++ /* 1 if 'authorization = recursive' */
+ static char *authen_default = NULL; /* top level authentication default */
+-static int authen_default_method = 0; /*For method check */
++ /* ='default authentication' */
++static int authen_default_method = 0; /* For method check */
++ /* ='default authentication' symbol */
+ static char *nopasswd_str = "nopassword";
+
+-/* A host definition structure. Currently unused, but when we start
+- configuring host-specific information e.g. per-host keys, this is
+- where it should be kept.
+-
+- The first 2 fields (name and hash) are used by the hash table
+- routines to hash this structure into a table. Do not (re)move them */
+-
+-struct host {
+- char *name; /* host name */
+- void *hash; /* hash table next pointer */
+- int line; /* line number defined on */
+- char *key; /* host spesific key */
+- char *type; /* host type */
+-};
+-
+-/* A user or group definition
+-
+- The first 2 fields (name and hash) are used by the hash table
+- routines to hash this structure into a table. Move them at your
+- peril */
+-
+-struct user {
+- char *name; /* username */
+- void *hash; /* hash table next pointer */
+- int line; /* line number defined on */
+- long flags; /* flags field */
+-
+-#define FLAG_ISUSER 1 /* this structure represents a user */
+-#define FLAG_ISGROUP 2 /* this structure represents a group */
+-#define FLAG_SEEN 4 /* for circular definition detection */
+-
+- char *full_name; /* users full name */
+- char *login; /* Login password */
+- int nopasswd; /* user requires no password */
+- char *global; /* password to use if none set */
+- char *member; /* group we are a member of */
+- char *expires; /* expiration date */
+- char *arap; /* our arap secret */
+- char *pap; /* our pap secret */
+- char *opap; /* our outbound pap secret */
+- char *chap; /* our chap secret */
+-#ifdef MSCHAP
+- char *mschap; /* our mschap secret */
+-#endif /* MSCHAP */
+- char *msg; /* a message for this user */
+- char *before_author; /* command to run before authorization */
+- char *after_author; /* command to run after authorization */
+- int svc_dflt; /* default authorization behaviour for svc or
+- * cmd */
+- NODE *svcs; /* pointer to svc nodes */
+-#ifdef MAXSESS
+- int maxsess; /* Max sessions/user */
+-#endif /* MAXSESS */
+- char *time; /* Timestamp */
+-};
+-
+-typedef struct host HOST;
+-typedef struct user USER;
+
+ /* Only the first 2 fields (name and hash) are used by the hash table
+ routines to hashh structures into a table.
+ */
+
+-union hash {
+- struct user u;
+- struct host h;
+-};
+-
+-typedef union hash HASH;
++static void *grouptable[HASH_TAB_SIZE]; /* Table of group declarations */
++static void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
++static void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
+-void *grouptable[HASH_TAB_SIZE];/* Table of group declarations */
+-void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
+-void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
++struct enlist_entity_item {
++ struct enlist_entity_item *next;
++ int parent_type; char *parent;
++ int child_type; char * child; /* will be created when not found (for "enlist") */
++ struct expr *when;
++ int line;
++};
++static struct enlist_entity_item * enlist_entity_list;
++static struct enlist_entity_item **enlist_entity_list_tailp = &enlist_entity_list;
+
+-static void
+- sym_get();
+
++static void parse_error TAC_ARGS((char *fmt,...)) G_GNUC_PRINTF(1, 2);
+
+ #ifdef __STDC__
++
+ #include /* ANSI C, variable length args */
+ static void
+ parse_error(char *fmt,...)
+-#else
++
++#else /* __STDC__ */
++
+ #include /* has 'vararg' definitions */
+ /* VARARGS2 */
+ static void
+ parse_error(fmt, va_alist)
+ char *fmt;
+-
+ va_dcl /* no terminating semi-colon */
+-#endif
++
++#endif /* __STDC__ */
+ {
+ char msg[256]; /* temporary string */
+ va_list ap;
+@@ -220,7 +264,9 @@ va_dcl /* no terminating semi-colon */
+ tac_exit(1);
+ }
+
+-char *
++const char *cfg_nodestring TAC_ARGS((int type));
++
++const char *
+ cfg_nodestring(type)
+ int type;
+ {
+@@ -250,6 +296,54 @@ cfg_nodestring(type)
+ }
+ }
+
++const char *entity_type_to_string TAC_ARGS((int entity_type));
++
++const char *
++entity_type_to_string(entity_type)
++int entity_type;
++{
++ switch (entity_type) {
++ case S_user:
++ return ("user");
++ case S_host:
++ return ("host");
++ case S_group:
++ return ("group");
++ }
++ return (NULL);
++}
++
++static void **entity_type_to_hashtable TAC_ARGS((int entity_type));
++
++static void **
++entity_type_to_hashtable(entity_type)
++int entity_type;
++{
++ switch (entity_type) {
++ case S_user:
++ return (usertable);
++ case S_host:
++ return (hosttable);
++ case S_group:
++ return (grouptable);
++ }
++ return (NULL);
++}
++
++void scan_invalidate_entities TAC_ARGS((enum invalidate_scan what));
++
++void
++scan_invalidate_entities(what)
++enum invalidate_scan what;
++{
++ scan_invalidate_entities_hashtable( usertable, what);
++ scan_invalidate_entities_hashtable( hosttable, what);
++ scan_invalidate_entities_hashtable(grouptable, what);
++}
++
++
++static void free_attrs TAC_ARGS((NODE *node));
++
+ static void
+ free_attrs(node)
+ NODE *node;
+@@ -257,13 +351,17 @@ NODE *node;
+ NODE *next;
+
+ while (node) {
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
++
+ switch (node->type) {
+ case N_optarg:
+ case N_arg:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+- node->value);
++ (const char *) node->value);
+ break;
+ default:
+ report(LOG_ERR, "Illegal node type %s for free_attrs",
+@@ -278,6 +376,8 @@ NODE *node;
+ }
+ }
+
++static void free_cmd_matches TAC_ARGS((NODE *node));
++
+ static void
+ free_cmd_matches(node)
+ NODE *node;
+@@ -288,16 +388,32 @@ NODE *node;
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+- node->value);
++ (const char *) node->value);
++
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
+
+ free(node->value); /* text */
+- free(node->value1); /* regexp compiled text */
++
++#ifdef WITH_INCLUDED_REGEX
++
++ free(node->value1); /* tac_regexp compiled text */
++
++#else /* WITH_INCLUDED_REGEX */
++
++ regfree((regex_t *) node->value1); /* POSIX regex compiled text */
++
++#endif /* WITH_INCLUDED_REGEX */
++
+ next = node->next;
+ free(node);
+ node = next;
+ }
+ }
+
++static void free_svcs TAC_ARGS((NODE *node));
++
+ static void
+ free_svcs(node)
+ NODE *node;
+@@ -305,12 +421,16 @@ NODE *node;
+ NODE *next;
+
+ while (node) {
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
+
+ switch (node->type) {
+ case N_svc_cmd:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+- cfg_nodestring(node->type), node->value);
++ cfg_nodestring(node->type),
++ (const char *) node->value);
+ free(node->value); /* cmd name */
+ free_cmd_matches(node->value1);
+ next = node->next;
+@@ -340,85 +460,107 @@ NODE *node;
+ }
+ }
+
++static void free_enlist_entity_item TAC_ARGS((struct enlist_entity_item *item));
++
++static void
++free_enlist_entity_item(item)
++struct enlist_entity_item *item;
++{
++ free(item->parent);
++ free(item->child);
++ free_expr(item->when);
++}
++
++
++static void free_entity TAC_ARGS((ENTITY *entity));
++
+ static void
+-free_userstruct(user)
+-USER *user;
++free_entity(entity)
++ENTITY *entity;
+ {
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+- (user->flags & FLAG_ISUSER) ? "user" : "group",
+- user->name);
+-
+- if (user->name)
+- free(user->name);
+- if (user->full_name)
+- free(user->full_name);
+- if (user->login)
+- free(user->login);
+- if (user->member)
+- free(user->member);
+- if (user->expires)
+- free(user->expires);
+- if (user->time)
+- free(user->time);
+- if (user->arap)
+- free(user->arap);
+- if (user->chap)
+- free(user->chap);
++ entity_type_to_string(entity->type), entity->name);
++
++ /* function MUST be called while the whole entity is still VALID! */
++ scan_free_entity(entity);
++
++ if (entity->name)
++ free(entity->name);
++ if (entity->full_name)
++ free(entity->full_name);
++ if (entity->login)
++ free(entity->login);
++ if (entity->expires)
++ free(entity->expires);
++ if (entity->time)
++ free(entity->time);
++ if (entity->arap)
++ free(entity->arap);
++ if (entity->chap)
++ free(entity->chap);
+ #ifdef MSCHAP
+- if (user->mschap)
+- free(user->mschap);
++ if (entity->mschap)
++ free(entity->mschap);
+ #endif /* MSCHAP */
+- if (user->pap)
+- free(user->pap);
+- if (user->opap)
+- free(user->opap);
+- if (user->global)
+- free(user->global);
+- if (user->msg)
+- free(user->msg);
+- if (user->before_author)
+- free(user->before_author);
+- if (user->after_author)
+- free(user->after_author);
+- free_svcs(user->svcs);
++ if (entity->pap)
++ free(entity->pap);
++ if (entity->opap)
++ free(entity->opap);
++ if (entity->global)
++ free(entity->global);
++ if (entity->msg)
++ free(entity->msg);
++ if (entity->before_author)
++ free(entity->before_author);
++ if (entity->after_author)
++ free(entity->after_author);
++ if (entity->key)
++ free(entity->key);
++ free_svcs(entity->svcs);
+ }
+
++static void free_hashtable TAC_ARGS((void **hashtable));
++
+ static void
+-free_hoststruct(host)
+-HOST *host;
++free_hashtable(hashtable)
++void **hashtable;
+ {
+- if (debug & DEBUG_CLEAN_FLAG)
+- report(LOG_DEBUG, "free %s",
+- host->name);
+-
+- if (host->name)
+- free(host->name);
+-
+- if (host->key)
+- free(host->key);
++ int i;
++ ENTITY *entity,**entityp;
+
+- if (host->type)
+- free(host->type);
++ for (i = 0; i < HASH_TAB_SIZE; i++) {
++ entityp = (ENTITY **) (hashtable+i);
++ while ((entity = *entityp)) {
++ *entityp = entity->hash;
++ free_entity(entity);
++ free(entity);
++ }
++ }
+ }
+
++
+ /*
+ * Exported routines
+ */
+
++void cfg_clean_config TAC_ARGS((void));
++
+ /* Free all allocated structures preparatory to re-reading the config file */
+ void
+ cfg_clean_config()
+ {
+- int i;
+- USER *entry, *next;
+- HOST *host_entry,*hn;
++ struct enlist_entity_item *enlist_entity_item;
+
+ if (authen_default) {
+ free(authen_default);
+ authen_default = NULL;
+ }
+
++ if (algorithm_recursive) {
++ algorithm_recursive = 0;
++ }
++
+ if (authen_default_method) {
+ authen_default_method = 0;
+ }
+@@ -438,43 +580,21 @@ cfg_clean_config()
+ session.db_acct = NULL;
+ }
+
+- /* clean the hosttable */
+- for (i = 0; i < HASH_TAB_SIZE; i++) {
+- host_entry = (HOST *) hosttable[i];
+- while (host_entry) {
+- hn = host_entry->hash;
+- free_hoststruct(host_entry);
+- free(host_entry);
+- host_entry = hn;
+- }
+- hosttable[i] = NULL;
+- }
+-
+- /* the grouptable */
+- for (i = 0; i < HASH_TAB_SIZE; i++) {
+- entry = (USER *) grouptable[i];
+- while (entry) {
+- next = entry->hash;
+- free_userstruct(entry);
+- free(entry);
+- entry = next;
+- }
+- grouptable[i] = NULL;
+- }
++ free_hashtable( usertable);
++ free_hashtable( hosttable);
++ free_hashtable(grouptable);
+
+- /* the usertable */
+- for (i = 0; i < HASH_TAB_SIZE; i++) {
+- entry = (USER *) usertable[i];
+- while (entry) {
+- next = entry->hash;
+- free_userstruct(entry);
+- free(entry);
+- entry = next;
+- }
+- usertable[i] = NULL;
++ while (enlist_entity_list) {
++ enlist_entity_item = enlist_entity_list;
++ enlist_entity_list = enlist_entity_item->next;
++ free_enlist_entity_item(enlist_entity_item);
++ free(enlist_entity_item);
+ }
++ enlist_entity_list_tailp = &enlist_entity_list;
+ }
+
++static int parse_permission TAC_ARGS((void));
++
+ static int
+ parse_permission()
+ {
+@@ -490,6 +610,8 @@ parse_permission()
+ return (symbol);
+ }
+
++static int parse TAC_ARGS((int symbol));
++
+ static int
+ parse(symbol)
+ int symbol;
+@@ -505,9 +627,13 @@ int symbol;
+ return (0);
+ }
+
++static int parse_opt_svc_default TAC_ARGS((void));
++
+ static int
+ parse_opt_svc_default()
+ {
++ int retval;
++
+ if (sym_code != S_default) {
+ return (0);
+ }
+@@ -515,14 +641,30 @@ parse_opt_svc_default()
+ parse(S_default);
+ parse(S_svc);
+ parse(S_separator);
+- if (sym_code == S_permit) {
+- parse(S_permit);
+- return (S_permit);
++
++ switch (sym_code) {
++ default:
++ parse_error("expecting 'permit', 'deny' or 'default' but found '%s' on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_default:
++ if (!algorithm_recursive) {
++ parse_error("'default service = %s' supported only if set top level 'authorization = recursive', on line %d",
++ sym_buf, sym_line);
++ return (1);
++ }
++ /* FALLTHRU */
++ case S_permit:
++ case S_deny:
++ retval = sym_code;
++ sym_get();
++ return (retval);
+ }
+- parse(S_deny);
+- return (S_deny);
+ }
+
++static int parse_opt_attr_default TAC_ARGS((void));
++
+ static int
+ parse_opt_attr_default()
+ {
+@@ -536,20 +678,19 @@ parse_opt_attr_default()
+ return (S_permit);
+ }
+
+-static int parse_user();
+-static int parse_host();
+-
+-static void
+- rch();
+-
+ /*
+ Parse lines in the config file, creating data structures
+ Return 1 on error, otherwise 0 */
+
++static int parse_decls TAC_ARGS((void));
++
+ static int
+ parse_decls()
+ {
+ no_user_dflt = 0; /* default if user doesn't exist */
++ algorithm_recursive = 0; /* use backward compatible alg. by default */
++ when_expr_root_init();
++ enlist_entity_list_tailp = &enlist_entity_list;
+
+ sym_code = 0;
+ rch();
+@@ -614,7 +755,7 @@ parse_decls()
+ case S_db:
+ #endif
+ #ifdef USE_LDAP
+- case S_ldap;
++ case S_ldap:
+ #endif
+ #ifdef USE_PAM
+ case S_pam:
+@@ -642,6 +783,26 @@ parse_decls()
+ continue;
+ }
+
++ case S_authorization:
++ sym_get();
++ parse(S_separator);
++ switch (sym_code) {
++ default:
++ parse_error("expecting 'first' or 'recursive' but found '%s' on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_first:
++ parse(S_first);
++ algorithm_recursive = 0;
++ continue;
++
++ case S_recursive:
++ parse(S_recursive);
++ algorithm_recursive = 1;
++ continue;
++ }
++
+ case S_key:
+ /* Process a key declaration. */
+ sym_get();
+@@ -656,17 +817,12 @@ parse_decls()
+ sym_get();
+ continue;
+
+- case S_host:
+- parse_host();
+- continue;
+-
+ case S_user:
++ case S_host:
+ case S_group:
+- parse_user();
++ parse_entity(sym_code);
+ continue;
+
+- /* case S_host: parse_host(); continue; */
+-
+ default:
+ parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
+ return (1);
+@@ -674,8 +830,6 @@ parse_decls()
+ }
+ }
+
+-static NODE *parse_svcs();
+-
+ /* Assign a value to a field. Issue an error message and return 1 if
+ it's already been assigned. This is a macro because I was sick of
+ repeating the same code fragment over and over */
+@@ -688,147 +842,511 @@ sym_get(); parse(S_separator); if (field) { \
+ } \
+ field = tac_strdup(sym_buf);
+
+-static int
+-parse_host()
++static struct expr *when_expr_root;
++#define WHEN_EXPR_ROOT_SANE() \
++ (when_expr_root && !when_expr_root->next && when_expr_root->type==S_and)
++#define WHEN_EXPR_ROOT_EMPTY() \
++ (WHEN_EXPR_ROOT_SANE() && !when_expr_root->u.and_or.child_first)
++
++static struct expr *parse_expr_node TAC_ARGS((int single_item));
++
++static struct expr *
++parse_expr_node(single_item)
++int single_item;
+ {
+- HOST *h;
+- HOST *host = (HOST *) tac_malloc(sizeof(HOST));
+- int save_sym;
+- char buf[MAX_INPUT_LINE_LEN];
++ struct expr *expr_root = NULL;
++ struct expr **succ_exprp = &expr_root;
++ struct expr *expr;
+
+- bzero(host, sizeof(HOST));
++ while (1) {
++ switch (sym_code) {
+
++ case S_not:
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ *succ_exprp = expr;
++ expr->next = NULL;
++ succ_exprp = &expr->next;
++ expr->type = S_not;
+ sym_get();
+- parse(S_separator);
+- host->name = tac_strdup(sym_buf);
+- host->line = sym_line;
++ expr->u.not.child = parse_expr_node(1 /* single_item */);
++ if (!expr->u.not.child) {
++ free_expr(expr_root);
++ return (NULL);
++ }
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ *succ_exprp = expr;
++ expr->next = NULL;
++ succ_exprp = &expr->next;
++ expr->type = sym_code;
++ sym_get();
++ expr->u.entity.name = tac_strdup(sym_buf);
++ sym_get();
++ expr->u.entity.entity = NULL; /* not known yet */
++ break;
+
+- h = hash_add_entry(hosttable, (void *) host);
++ case S_openparen:
++ sym_get();
++ expr = parse_expr_node(0 /* single_item */);
++ *succ_exprp = expr;
++ parse(S_closeparen);
+
+- if (h) {
+- parse_error("multiply defined %s on lines %d and %d",
+- host->name, h->line, sym_line);
+- return (1);
++ if (expr->next) {
++ report(LOG_ERR, "Illegal filled next field of parsed parenthesed expr");
++ free_expr(expr_root);
++ return (NULL);
+ }
++ succ_exprp = &expr->next;
++ break;
+
+- sym_get();
+- parse(S_openbra);
++ default:
++ parse_error("expecting 'not', 'user', 'host', 'group' or '(' but found '%s' on line %d",
++ sym_buf, sym_line);
++ free_expr(expr_root);
++ return (NULL);
++ }
++
++ if (single_item) /* used by 'not' operator with high precedence */
++ return (expr_root);
+
+- while (1) {
+ switch (sym_code) {
+- case S_eof:
+- return (0);
+- case S_key:
+- ASSIGN(host->key);
+- sym_get();
+- continue;
+- case S_type:
+- ASSIGN(host->type);
++
++ case S_and:
++ case S_or:
++ if (expr_root->type == (sym_code==S_and ? S_or : S_and)) {
++ parse_error("ambiguous use of 'and' together with 'or', parentheses required on line %d",
++ sym_line);
++ free_expr(expr_root);
++ return (NULL);
++ }
++ if (expr_root->type != sym_code) {
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ expr->next = NULL;
++ expr->type = sym_code;
++ expr->u.and_or.child_first = expr_root;
++ expr_root = expr;
++ }
+ sym_get();
+ continue;
++ }
+
+- case S_closebra:
+- parse(S_closebra);
+- return (0);
++ return(expr_root);
++ }
++}
+
+- default:
+- parse_error("Unrecognised keyword %s for host %s on line %d",
+- sym_buf, host->name,sym_line);
++static struct expr *parse_when_decl TAC_ARGS((void));
+
+- return (0);
++static struct expr *
++parse_when_decl()
++{
++ parse(S_when);
++ if (!algorithm_recursive) {
++ parse_error("'when' conditionals supported only if set top level 'authorization = recursive', on line %d",
++ sym_line);
++ return (NULL);
+ }
+- } /* while */
+-} /* finish parse_host */
++ parse(S_separator);
++ return (parse_expr_node(0 /* single_item */));
++}
++
++static void when_expr_root_init TAC_ARGS((void));
++
++static void
++when_expr_root_init()
++{
++ free_expr(when_expr_root);
++ when_expr_root = new_expr(S_and);
++}
+
++static int push_parsed_when_decl TAC_ARGS((void));
+
+ static int
+-parse_user()
++push_parsed_when_decl()
+ {
+- USER *n;
+- int isuser;
+- USER *user = (USER *) tac_malloc(sizeof(USER));
+- int save_sym;
+- char **fieldp;
+- char buf[MAX_INPUT_LINE_LEN];
++ struct expr *parsed_expr;
+
+- bzero(user, sizeof(USER));
++ parsed_expr = parse_when_decl();
++ if (!parsed_expr)
++ return (1);
++ if (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_ERR, "INTERNAL: when_expr_root not valid during push_parsed_when_decl()!");
++ free_expr(parsed_expr);
++ return (1);
++ }
++ if (parsed_expr->next) {
++ report(LOG_ERR, "INTERNAL: Illegal filled next field of parsed expr");
++ free_expr(parsed_expr);
++ return (1);
++ }
++ parsed_expr->next = when_expr_root->u.and_or.child_first;
++ when_expr_root->u.and_or.child_first = parsed_expr;
++ when_expr_root->line = parsed_expr->line;
++ return (0);
++}
+
+- isuser = (sym_code == S_user);
++static int pop_when_decl TAC_ARGS((void));
+
+- sym_get();
+- parse(S_separator);
+- user->name = tac_strdup(sym_buf);
+- user->line = sym_line;
++static int
++pop_when_decl()
++{
++ struct expr *first_expr;
+
+- if (isuser) {
+- user->flags |= FLAG_ISUSER;
+- n = hash_add_entry(usertable, (void *) user);
+- } else {
+- user->flags |= FLAG_ISGROUP;
+- n = hash_add_entry(grouptable, (void *) user);
++ if (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_ERR, "INTERNAL: when_expr_root not valid during pop_when_decl()!");
++ return (1);
+ }
+-
+- if (n) {
+- parse_error("multiply defined %s %s on lines %d and %d",
+- isuser ? "user" : "group",
+- user->name, n->line, sym_line);
++ first_expr = when_expr_root->u.and_or.child_first;
++ if (!first_expr) {
++ report(LOG_ERR, "No expr in stack and pop_when_decl() called");
+ return (1);
+ }
+- sym_get();
+- parse(S_openbra);
++ when_expr_root->u.and_or.child_first = first_expr->next;
++ first_expr->next = NULL;
++ free_expr(first_expr);
++ return (0);
++}
+
+- /* Is the default deny for svcs or cmds to be overridden? */
+- user->svc_dflt = parse_opt_svc_default();
++static struct expr *copy_current_when_decl TAC_ARGS((void));
+
+- while (1) {
+- switch (sym_code) {
+- case S_eof:
+- return (0);
++static struct expr *
++copy_current_when_decl()
++{
++ return (dupl_expr(when_expr_root));
++}
+
+- case S_time:
+- ASSIGN(user->time);
+- sym_get();
+- continue;
++static struct expr *when_expr_dungeon;
+
+- case S_before:
+- sym_get();
+- parse(S_authorization);
+- if (user->before_author)
+- free(user->before_author);
+- user->before_author = tac_strdup(sym_buf);
++static void starve_when_decl TAC_ARGS((void));
++
++static void
++starve_when_decl()
++{
++ if (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_WARNING, "INTERNAL: when_expr_root not sane during starve_when_decl!");
++ }
++ when_expr_root->next = when_expr_dungeon;
++ when_expr_dungeon = when_expr_root;
++ when_expr_root = NULL;
++ when_expr_root_init();
++}
++
++static int feed_when_decl TAC_ARGS((void));
++
++static int
++feed_when_decl()
++{
++ if (!when_expr_dungeon) {
++ report(LOG_ERR, "INTERNAL: No expr in dungeon and feed_when_decl() called");
++ return (1);
++ }
++ if (!WHEN_EXPR_ROOT_EMPTY()) {
++ report(LOG_WARNING, "INTERNAL: Some 'when' expression found still pushed in dungeon during feed_when_decl()!");
++ }
++ free_expr(when_expr_root);
++ when_expr_root = when_expr_dungeon;
++ when_expr_dungeon = when_expr_dungeon->next;
++ when_expr_root->next = NULL;
++ return (0);
++}
++
++ENTITY *entity_lookup TAC_ARGS((int type, const char *name));
++
++ENTITY *
++entity_lookup(type, name)
++int type;
++const char *name;
++{
++ return (hash_lookup(entity_type_to_hashtable(type), name));
++}
++
++static int enlist_entity_connect TAC_ARGS((void));
++
++static int
++enlist_entity_connect()
++{
++ struct enlist_entity_item *item;
++ ENTITY *parent_entity, *child_entity;
++
++ while ((item=enlist_entity_list)) {
++
++ parent_entity = entity_lookup(item->parent_type, item->parent);
++ if (!parent_entity) {
++ parse_error("Entity %s %s not defined, referenced as parent on line %d",
++ entity_type_to_string(item->parent_type), item->parent, item->line);
++ return (1);
++ }
++ child_entity = entity_lookup(item-> child_type, item-> child);
++ if (!child_entity) {
++ child_entity = new_entity(item->child_type, item->child, item->line);
++ if (!child_entity)
++ return (1); /* 'hash_add_entry()' conflict */
++ item->child = NULL; /* don't free string ref'ed from 'child_entity'! */
++ }
++
++ if (!enlist_entity_direct(parent_entity, child_entity, item->when))
++ return (1); /* entities not found */
++
++ enlist_entity_list = item->next;
++ item->when = NULL;
++ free_enlist_entity_item(item);
++ free(item);
++ }
++ enlist_entity_list_tailp = &enlist_entity_list;
++ return (0);
++}
++
++static void enlist_entity TAC_ARGS((int parent_type, const char *parent, int child_type, const char *child));
++
++static void
++enlist_entity(parent_type, parent, child_type, child)
++int parent_type;
++const char *parent;
++int child_type;
++const char *child;
++{
++ struct enlist_entity_item *item =
++ (struct enlist_entity_item *) tac_malloc(sizeof(struct enlist_entity_item));
++
++ item->next = NULL;
++ *enlist_entity_list_tailp = item;
++ enlist_entity_list_tailp = &item->next;
++
++ item->parent_type = parent_type;
++ item->parent = tac_strdup(parent);
++ item-> child_type = child_type;
++ item->child = tac_strdup(child);
++ item->when = copy_current_when_decl();
++ item->line = sym_line;
++}
++
++static int parse_entity_spec TAC_ARGS((void));
++
++/* returns 0 for error, otherwise S_user, S_host or S_group; sym_buf filled */
++static int
++parse_entity_spec()
++{
++ int retval;
++
++ if (sym_code != S_user
++ && sym_code != S_host
++ && sym_code != S_group
++ ) {
++ parse_error("Expecting 'user', 'host' or ' group' as entity specification, found %s on line %d",
++ sym_buf, sym_line);
++ return (0);
++ }
++
++ retval = sym_code;
+ sym_get();
+- continue;
+
+- case S_after:
++ return (retval);
++}
++
++static int parse_conditional_block_item TAC_ARGS((ENTITY *entity));
++
++static int
++parse_conditional_block_item(entity)
++ENTITY *entity;
++{
++ switch (sym_code) {
++ case S_eof:
++ return (1);
++
++ /* case S_closebra: not needed, handled by our caller parse_conditional_block() */
++
++ default:
++ parse_error("Unrecognised keyword %s for entity on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_member:
+ sym_get();
+- parse(S_authorization);
+- if (user->after_author)
+- free(user->after_author);
+- user->after_author = tac_strdup(sym_buf);
++ parse(S_separator);
++ enlist_entity(S_group, sym_buf, entity->type, entity->name);
+ sym_get();
+- continue;
++ break;
++
++ case S_enlist: {
++ int parsed_entity_type;
++
++ if (entity->type != S_group) {
++ parse_error("'enlist' keyword allowed only in 'group' section on line %d",
++ sym_line);
++ return (1);
++ }
++ sym_get();
++ parse(S_separator);
++ parsed_entity_type = parse_entity_spec();
++ if (!parsed_entity_type)
++ return (1);
++ enlist_entity(entity->type, entity->name, parsed_entity_type, sym_buf);
++ sym_get();
++ break;
++ }
+
+ case S_svc:
+ case S_cmd:
+
+- if (user->svcs) {
++ if (entity->svcs) {
+ /*
+ * Already parsed some services/commands. Thanks to Gabor Kiss
+ * who found this bug.
+ */
+ NODE *p;
+- for (p=user->svcs; p->next; p=p->next)
++ for (p=entity->svcs; p->next; p=p->next)
+ /* NULL STMT */;
+ p->next = parse_svcs();
+ } else {
+- user->svcs = parse_svcs();
++ entity->svcs = parse_svcs();
++ }
++ break;
++
++ case S_when:
++ if (parse_conditional_block(entity))
++ return (1);
++ break;
++ }
++
++ return (0);
++}
++
++static int parse_conditional_block TAC_ARGS((ENTITY *entity));
++
++static int
++parse_conditional_block(entity)
++ENTITY *entity;
++{
++ int retval = -1 /* GCC paranoia */;
++
++ if (push_parsed_when_decl())
++ return (1);
++ parse(S_openbra);
++
++ while (1) {
++ if (sym_code == S_closebra) {
++ sym_get();
++ retval = 0; /* success */
++ break;
++ }
++
++ if (parse_conditional_block_item(entity)) {
++ retval = 1; /* failure */
++ break;
++ }
++ }
++
++ if (pop_when_decl())
++ return (1);
++
++ return (retval);
++}
++
++/* passed 'name' WILL be directly stored to returned ENTITY, don't touch it! */
++
++ENTITY *new_entity TAC_ARGS((int type, char *name, int line));
++
++ENTITY *
++new_entity(type, name, line)
++int type;
++char *name;
++int line;
++{
++ ENTITY *entity = (ENTITY *) tac_malloc(sizeof(ENTITY));
++ ENTITY *hash_conflict;
++
++ bzero(entity, sizeof(ENTITY));
++ tac_list_init(&entity->to_parent_membership_list);
++ tac_list_init(&entity->to_child_membership_list );
++ entity->to_child_membership_num = 0;
++ scan_init_entity(entity);
++
++ entity->type = type;
++ entity->name = name;
++ entity->line = line;
++
++ hash_conflict = hash_add_entry(entity_type_to_hashtable(type), (void *) entity);
++ if (hash_conflict) {
++ parse_error("multiply defined %s %s on lines %d and %d",
++ entity_type_to_string(type),
++ entity->name, hash_conflict->line, sym_line);
++ free (entity);
++ return (NULL);
++ }
++
++ return (entity);
++}
++
++static int parse_entity TAC_ARGS((int entity_type));
++
++static int
++parse_entity(entity_type)
++int entity_type;
++{
++ ENTITY *entity;
++ int save_sym;
++ char **fieldp = NULL /* GCC paranoia */;
++ char buf[MAX_INPUT_LINE_LEN];
++
++ sym_get();
++ parse(S_separator);
++
++ entity = new_entity(entity_type, tac_strdup(sym_buf) /* name */, sym_line /* line */);
++ if (!entity)
++ return (1); /* 'hash_add_entry()' conflict, 'tac_strdup(sym_buf)' leaked! */
++
++ sym_get();
++ parse(S_openbra);
++
++ /* Is the default deny for svcs or cmds to be overridden? */
++ entity->svc_dflt = parse_opt_svc_default();
++
++ while (1) {
++ if (entity_type != S_user)
++ switch (sym_code) {
++ case S_key:
++ ASSIGN(entity->key);
++ sym_get();
++ continue;
+ }
++
++ switch (sym_code) {
++ case S_eof:
++ return (0);
++
++ case S_time:
++ ASSIGN(entity->time);
++ sym_get();
++ continue;
++
++ case S_before:
++ sym_get();
++ parse(S_authorization);
++ if (entity->before_author)
++ free(entity->before_author);
++ entity->before_author = tac_strdup(sym_buf);
++ sym_get();
++ continue;
++
++ case S_after:
++ sym_get();
++ parse(S_authorization);
++ if (entity->after_author)
++ free(entity->after_author);
++ entity->after_author = tac_strdup(sym_buf);
++ sym_get();
+ continue;
+
+ case S_login:
+- if (user->login) {
++ if (entity->login) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+- codestring(sym_code), user->login,
++ codestring(sym_code), entity->login,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+@@ -837,15 +1355,15 @@ parse_user()
+ switch(sym_code) {
+
+ case S_skey:
+- user->login = tac_strdup(sym_buf);
++ entity->login = tac_strdup(sym_buf);
+ break;
+
+ case S_nopasswd:
+ /* set to dummy string, so that we detect a duplicate
+ * password definition attempt
+ */
+- user->login = tac_strdup(nopasswd_str);
+- user->nopasswd = 1;
++ entity->login = tac_strdup(nopasswd_str);
++ entity->nopasswd = 1;
+ break;
+
+ case S_file:
+@@ -860,7 +1378,7 @@ parse_user()
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+- user->login = tac_strdup(buf);
++ entity->login = tac_strdup(buf);
+ break;
+
+ default:
+@@ -878,9 +1396,9 @@ parse_user()
+ continue;
+
+ case S_pap:
+- if (user->pap) {
++ if (entity->pap) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+- codestring(sym_code), user->pap,
++ codestring(sym_code), entity->pap,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+@@ -896,11 +1414,11 @@ parse_user()
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+- user->pap = tac_strdup(buf);
++ entity->pap = tac_strdup(buf);
+ break;
+
+ sprintf(buf, "%s ", sym_buf);
+- user->pap = tac_strdup(buf);
++ entity->pap = tac_strdup(buf);
+ break;
+
+ default:
+@@ -918,23 +1436,17 @@ parse_user()
+ continue;
+
+ case S_name:
+- ASSIGN(user->full_name);
+- sym_get();
+- continue;
+-
+- case S_member:
+- ASSIGN(user->member);
++ ASSIGN(entity->full_name);
+ sym_get();
+ continue;
+
+-
+ case S_expires:
+- ASSIGN(user->expires);
++ ASSIGN(entity->expires);
+ sym_get();
+ continue;
+
+ case S_message:
+- ASSIGN(user->msg);
++ ASSIGN(entity->msg);
+ sym_get();
+ continue;
+
+@@ -952,20 +1464,32 @@ parse_user()
+ parse(S_cleartext);
+ strcat(buf, sym_buf);
+
+- if (save_sym == S_arap)
+- fieldp = &user->arap;
+- if (save_sym == S_chap)
+- fieldp = &user->chap;
++ switch (save_sym) {
++ case S_arap:
++ fieldp = &entity->arap;
++ break;
++ case S_chap:
++ fieldp = &entity->chap;
++ break;
+ #ifdef MSCHAP
+- if (save_sym == S_mschap)
+- fieldp = &user->mschap;
++ case S_mschap:
++ fieldp = &entity->mschap;
++ break;
+ #endif /* MSCHAP */
+- if (save_sym == S_pap)
+- fieldp = &user->pap;
+- if (save_sym == S_opap)
+- fieldp = &user->opap;
+- if (save_sym == S_global)
+- fieldp = &user->global;
++ case S_pap:
++ fieldp = &entity->pap;
++ break;
++ case S_opap:
++ fieldp = &entity->opap;
++ break;
++ case S_global:
++ fieldp = &entity->global;
++ break;
++ default:
++ report(LOG_ERR, "INTERNAL: fieldp not recognized (on line %d)",
++ sym_line);
++ continue;
++ }
+
+ if (*fieldp) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+@@ -984,7 +1508,7 @@ parse_user()
+ case S_maxsess:
+ sym_get();
+ parse(S_separator);
+- if (sscanf(sym_buf, "%d", &user->maxsess) != 1) {
++ if (sscanf(sym_buf, "%d", &entity->maxsess) != 1) {
+ parse_error("expecting integer, found '%s' on line %d",
+ sym_buf, sym_line);
+ }
+@@ -997,16 +1521,14 @@ parse_user()
+ fprintf(stderr,
+ "\npassword = is obsolete. Use login = des \n");
+ }
+- parse_error("Unrecognised keyword %s for user on line %d",
+- sym_buf, sym_line);
+
+- return (0);
++ if (parse_conditional_block_item(entity))
++ return (0); /* error message already printed */
+ }
+ }
+ }
+
+-static NODE *parse_attrs();
+-static NODE *parse_cmd_matches();
++static NODE *parse_svcs TAC_ARGS((void));
+
+ static NODE *
+ parse_svcs()
+@@ -1034,11 +1556,16 @@ parse_svcs()
+
+ sym_get();
+ parse(S_openbra);
+-
++ starve_when_decl();
+ result->value1 = parse_cmd_matches();
++ parse(S_closebra);
++ if (feed_when_decl())
++ tac_exit(1); /* no error return possibility */
++
+ result->type = N_svc_cmd;
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
+
+- parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+ }
+@@ -1075,25 +1602,52 @@ parse_svcs()
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ }
++
+ sym_get();
+ parse(S_openbra);
++ starve_when_decl();
++
+ result->dflt = parse_opt_attr_default();
+ result->value = parse_attrs();
++
+ parse(S_closebra);
++ feed_when_decl();
++
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
++
+ result->next = parse_svcs();
+ return (result);
+ }
+
+-/* := */
++/* := */
++
++static NODE *parse_cmd_matches TAC_ARGS((void));
+
+ static NODE *
+ parse_cmd_matches()
+ {
++ NODE *retval = NULL, **succp = &retval;
+ NODE *result;
+
+- if (sym_code != S_permit && sym_code != S_deny) {
+- return (NULL);
+- }
++ for (;;) {
++ switch (sym_code) {
++ default:
++ return (retval);
++
++ case S_when:
++ if (push_parsed_when_decl())
++ tac_exit(1); /* no error return possibility */
++ parse(S_openbra);
++ result = parse_cmd_matches();
++ parse(S_closebra);
++ if (pop_when_decl())
++ tac_exit(1); /* no error return possibility */
++ break;
++
++ case S_permit:
++ case S_deny:
++
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+@@ -1102,7 +1656,20 @@ parse_cmd_matches()
+ result->type = (parse_permission() == S_permit) ? N_permit : N_deny;
+ result->value = tac_strdup(sym_buf);
+
+- result->value1 = (void *) regcomp(result->value);
++#ifdef WITH_INCLUDED_REGEX
++
++ result->value1 = (void *) tac_regcomp(result->value);
++
++#else /* WITH_INCLUDED_REGEX */
++
++ result->value1 = tac_malloc(sizeof(regex_t));
++ if (regcomp(result->value1, result->value /* regex */, REG_NOSUB /* cflags */)) {
++ free(result->value1);
++ result->value1 = NULL;
++ }
++
++#endif /* WITH_INCLUDED_REGEX */
++
+ if (!result->value1) {
+ report(LOG_ERR, "in regular expression %s on line %d",
+ sym_buf, sym_line);
+@@ -1110,30 +1677,56 @@ parse_cmd_matches()
+ }
+ sym_get();
+
+- result->next = parse_cmd_matches();
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
+
+- return (result);
++ result->next = NULL;
++ }
++ *succp = result;
++ while (result->next)
++ result = result->next; /* skip parsed chain from parse_cmd_matches() */
++ succp = &result->next;
++ }
++ /* NOTREACHED */
+ }
+
++static NODE *parse_attrs TAC_ARGS((void));
++
+ static NODE *
+ parse_attrs()
+ {
++ NODE *retval = NULL, **succp = &retval;
+ NODE *result;
+ char buf[MAX_INPUT_LINE_LEN];
+- int optional = 0;
++ int optional;
+
+- if (sym_code == S_closebra) {
+- return (NULL);
+- }
++ for (;;) {
++ optional = 0;
++
++ switch (sym_code) {
++ case S_closebra:
++ return (retval);
++
++ case S_when:
++ if (push_parsed_when_decl())
++ tac_exit(1); /* no error return possibility */
++ parse(S_openbra);
++ result = parse_attrs();
++ parse(S_closebra);
++ if (pop_when_decl())
++ tac_exit(1); /* no error return possibility */
++ break;
++
++ case S_optional:
++ optional = 1;
++ sym_get();
++ /* FALLTHRU */
++ default:
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+- if (sym_code == S_optional) {
+- optional++;
+- sym_get();
+- }
+ result->type = optional ? N_optarg : N_arg;
+
+ strcpy(buf, sym_buf);
+@@ -1144,13 +1737,22 @@ parse_attrs()
+ parse(S_string);
+
+ result->value = tac_strdup(buf);
+- result->next = parse_attrs();
+- return (result);
++
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
++
++ result->next = NULL;
++ }
++ *succp = result;
++ while (result->next)
++ result = result->next; /* skip parsed chain from parse_attrs() */
++ succp = &result->next;
++ }
++ /* NOTREACHED */
+ }
+
+
+-static void
+- getsym();
++static void sym_get TAC_ARGS((void));
+
+ static void
+ sym_get()
+@@ -1163,9 +1765,11 @@ sym_get()
+ }
+ }
+
++static char *sym_buf_add TAC_ARGS((int c));
++
+ static char *
+ sym_buf_add(c)
+-char c;
++int c; /* promoted "char" type */
+ {
+ if (sym_pos >= MAX_INPUT_LINE_LEN) {
+ sym_buf[MAX_INPUT_LINE_LEN-1] = '\0';
+@@ -1180,6 +1784,8 @@ char c;
+ return(sym_buf);
+ }
+
++static void getsym TAC_ARGS((void));
++
+ static void
+ getsym()
+ {
+@@ -1220,6 +1826,18 @@ next:
+ rch();
+ return;
+
++ case '(':
++ strcpy(sym_buf, "(");
++ sym_code = S_openparen;
++ rch();
++ return;
++
++ case ')':
++ strcpy(sym_buf, ")");
++ sym_code = S_closeparen;
++ rch();
++ return;
++
+ case '#':
+ while ((sym_ch != '\n') && (sym_ch != EOF))
+ rch();
+@@ -1304,6 +1922,8 @@ next:
+ }
+ }
+
++static void rch TAC_ARGS((void));
++
+ static void
+ rch()
+ {
+@@ -1318,201 +1938,208 @@ rch()
+ }
+
+
+-/* For a user or group, find the value of a field. Does not recurse. */
+-VALUE
+-get_value(user, field)
+-USER *user;
++static VALUE get_value TAC_ARGS((ENTITY *entity, int field));
++
++/* Find the value of a field. Does not recurse. */
++static VALUE
++get_value(entity, field)
++ENTITY *entity;
+ int field;
+ {
+ VALUE v;
+
++ v.pval = NULL; /* do both just for sure... */
+ v.intval = 0;
+
+- if (!user) {
+- parse_error("get_value: illegal user");
++ if (!entity) {
++ parse_error("get_value: illegal entity");
+ return (v);
+ }
+ switch (field) {
+
+ case S_name:
+- v.pval = user->name;
++ v.pval = entity->name;
+ break;
+
+ case S_login:
+- v.pval = user->login;
++ v.pval = entity->login;
+ break;
+
+ case S_global:
+- v.pval = user->global;
+- break;
+-
+- case S_member:
+- v.pval = user->member;
++ v.pval = entity->global;
+ break;
+
+ case S_expires:
+- v.pval = user->expires;
++ v.pval = entity->expires;
+ break;
+
+ case S_arap:
+- v.pval = user->arap;
++ v.pval = entity->arap;
+ break;
+
+ case S_chap:
+- v.pval = user->chap;
++ v.pval = entity->chap;
+ break;
+
+ #ifdef MSCHAP
+ case S_mschap:
+- v.pval = user->mschap;
++ v.pval = entity->mschap;
+ break;
+ #endif /* MSCHAP */
+
+ case S_pap:
+- v.pval = user->pap;
++ v.pval = entity->pap;
+ break;
+
+ case S_opap:
+- v.pval = user->opap;
++ v.pval = entity->opap;
+ break;
+
+ case S_message:
+- v.pval = user->msg;
++ v.pval = entity->msg;
+ break;
+
+ case S_svc:
+- v.pval = user->svcs;
++ v.pval = entity->svcs;
+ break;
+
+ case S_before:
+- v.pval = user->before_author;
++ v.pval = entity->before_author;
+ break;
+
+ case S_after:
+- v.pval = user->after_author;
++ v.pval = entity->after_author;
+ break;
+
+ case S_svc_dflt:
+- v.intval = user->svc_dflt;
++ v.intval = entity->svc_dflt;
+ break;
+
+ #ifdef MAXSESS
+ case S_maxsess:
+- v.intval = user->maxsess;
++ v.intval = entity->maxsess;
+ break;
+ #endif
+
+ case S_nopasswd:
+- v.intval = user->nopasswd;
++ v.intval = entity->nopasswd;
+ break;
+
+ case S_time:
+- v.pval = user->time;
++ v.pval = entity->time;
+ break;
+
+- default:
++ case S_key:
++ if (entity->type == S_user) {
++ report(LOG_ERR, "get_value: S_key field not supported in %s %s",
++ entity_type_to_string(entity->type), entity->name);
++ v.pval = NULL;
++ return(v);
++ }
++ v.pval = entity->key;
++ break;
++
++ default:
+ report(LOG_ERR, "get_value: unknown field %d", field);
+ break;
+ }
+ return (v);
+ }
+
+-/* For host , find value of field. Doesn't recursive */
+-VALUE
+-get_hvalue(host, field)
+-HOST *host;
+-int field;
++
++/* Internal graph scanning routines */
++
++static enum value_scan_func_result value_scan TAC_ARGS((int type, const char *name, int recurse, value_scan_func_t func, void *func_data));
++
++static enum value_scan_func_result
++value_scan(type, name, recurse, func, func_data)
++int type;
++const char *name;
++int recurse;
++value_scan_func_t func;
++void *func_data;
+ {
+- VALUE v;
+- v.intval = 0;
+- if(!host) {
+- parse_error("get_hvalue: illegal host");
+- return (v);
+- }
+- switch (field) {
+- case S_name:
+- v.pval = host->name;
+- break;
++ ENTITY *entity;
+
+- case S_key:
+- v.pval = host->key;
+- break;
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "value_scan: find %s %s, recurse=%d",
++ entity_type_to_string(type), name, recurse);
+
+- default:
+- report(LOG_ERR, "get_value: unknown field %d", field);
+- break;
++ entity = entity_lookup(type, name);
++ if (!entity) {
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "value_scan: no %s named %s",
++ entity_type_to_string(type), name);
++ return (VSFR_CONTINUE);
+ }
+- return (v);
+-}
+
++ return (value_scan_entity(entity, recurse, func, func_data));
++}
+
+ /* For each user, check she doesn't circularly reference a
+ group. Return 1 if it does */
+-static int
+-circularity_check()
+-{
+- USER *user, *entry, *group;
+- USER **users = (USER **) hash_get_entries(usertable);
+- USER **groups = (USER **) hash_get_entries(grouptable);
+- USER **p, **q;
+
+- /* users */
+- for (p = users; *p; p++) {
+- user = *p;
++static int circularity_check_failed;
+
+- if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "circularity_check: user=%s", user->name);
++static void circularity_check_fail TAC_ARGS((struct membership *membership));
++
++static void
++circularity_check_fail(membership)
++struct membership *membership;
++{
++ ENTITY *entity;
++
++ circularity_check_failed = 1;
+
+- /* Initialise all groups "seen" flags to zero */
+- for (q = groups; *q; q++) {
+- group = *q;
+- group->flags &= ~FLAG_SEEN;
++ report(LOG_ERR, "recursively defined groups:");
++ while (membership) {
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership);
++ report(LOG_ERR, "%s %s",
++ entity_type_to_string(entity->type), entity->name);
++ membership = value_scan_backward(entity);
+ }
++}
+
+- entry = user;
++static enum value_scan_func_result circularity_check_func TAC_ARGS((ENTITY *entity, void *func_data));
+
+- while (entry) {
+- /* check groups we are a member of */
+- char *groupname = entry->member;
++static enum value_scan_func_result
++circularity_check_func(entity, func_data /* unused */)
++ENTITY *entity;
++void *func_data;
++{
++ /* only useful to speedup case of failure */
++ if (circularity_check_failed)
++ return (VSFR_FOUND);
+
+- if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "\tmember of group %s",
+- groupname ? groupname : "");
++ return (VSFR_CONTINUE);
++}
+
++static int circularity_check TAC_ARGS((void));
+
+- /* if not a member of any groups, go on to next user */
+- if (!groupname)
+- break;
++static int
++circularity_check()
++{
++ ENTITY *entity;
++ ENTITY **users_base = (ENTITY **) hash_get_entries(usertable);
++ ENTITY **users;
+
+- group = (USER *) hash_lookup(grouptable, groupname);
+- if (!group) {
+- report(LOG_ERR, "%s=%s, group %s does not exist",
+- (entry->flags & FLAG_ISUSER) ? "user" : "group",
+- entry->name, groupname);
+- free(users);
+- free(groups);
+- return (1);
+- }
+- if (group->flags & FLAG_SEEN) {
+- report(LOG_ERR, "recursively defined groups");
++ /* users */
++ for (users = users_base; *users; users++) {
++ entity = *users;
+
+- /* print all seen "groups" */
+- for (q = groups; *q; q++) {
+- group = *q;
+- if (group->flags & FLAG_SEEN)
+- report(LOG_ERR, "%s", group->name);
+- }
+- free(users);
+- free(groups);
+- return (1);
+- }
+- group->flags |= FLAG_SEEN; /* mark group as seen */
+- entry = group;
+- }
++ if (debug & DEBUG_PARSE_FLAG)
++ report(LOG_DEBUG, "circularity_check: user=%s", entity->name);
++
++ circularity_check_failed = 0;
++ value_scan_forward_seen_hook = circularity_check_fail;
++ value_scan_entity(entity, TAC_PLUS_RECURSE,
++ (value_scan_func_t) circularity_check_func, NULL /* func_data-unused */);
++ value_scan_forward_seen_hook = NULL;
++ if (circularity_check_failed)
++ break;
+ }
+- free(users);
+- free(groups);
+- return (0);
++ free(users_base);
++ return (circularity_check_failed);
+ }
+
+
+@@ -1525,147 +2152,85 @@ circularity_check()
+ Returns void * because it can return a string or a node pointer
+ (should really return a union pointer).
+ */
+-static VALUE
+-cfg_get_value(name, isuser, attr, recurse)
+-char *name;
+-int isuser, attr, recurse;
+-{
+- USER *user, *group;
+- VALUE value;
+
+- value.pval = NULL;
++static VALUE cfg_get_value_VALUE; /* private */
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_value: name=%s isuser=%d attr=%s rec=%d",
+- name, isuser, codestring(attr), recurse);
+-
+- /* find the user/group entry */
+-
+- user = (USER *) hash_lookup(isuser ? usertable : grouptable, name);
+-
+- if (!user) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name);
+- return (value);
+- }
++static enum value_scan_func_result cfg_get_value_func TAC_ARGS((ENTITY *entity, int *attrp));
+
++static enum value_scan_func_result
++cfg_get_value_func(entity,attrp /* func_data */)
++ENTITY *entity;
++int *attrp;
++{
+ /* found the entry. Lookup value from attr=value */
+- value = get_value(user, attr);
++ cfg_get_value_VALUE = get_value(entity, *attrp);
++ if (cfg_get_value_VALUE.pval)
++ return (VSFR_FOUND);
+
+- if (value.pval || !recurse) {
+- return (value);
+- }
+- /* no value. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++ return (VSFR_CONTINUE);
++}
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_value: recurse group = %s",
+- group->name);
++static VALUE cfg_get_value TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+- value = get_value(group, attr);
++static VALUE
++cfg_get_value(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
++{
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "cfg_get_value: type=%s name=%s attr=%s recurse=%d",
++ entity_type_to_string(type), name, codestring(attr), recurse);
+
+- if (value.pval) {
+- return (value);
+- }
+- /* still nothing. Check containing group and so on */
++ cfg_get_value_VALUE.pval = NULL;
++ value_scan(type, name, recurse,
++ (value_scan_func_t) cfg_get_value_func, &attr /* func_data */);
++ return (cfg_get_value_VALUE);
++}
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
+- }
+
+- /* no value for this user or her containing groups */
+- value.pval = NULL;
+- return (value);
+-}
++/* Wrappers for cfg_get_value:
++ */
+
++int cfg_get_intvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+-/* Wrappers for cfg_get_value */
+ int
+-cfg_get_intvalue(name, isuser, attr, recurse)
+-char *name;
+-int isuser, attr, recurse;
++cfg_get_intvalue(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
+ {
+- int val = cfg_get_value(name, isuser, attr, recurse).intval;
++ int val = cfg_get_value(type, name, attr, recurse).intval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val);
+ return(val);
+ }
+
+-char *
+-cfg_get_pvalue(name, isuser, attr, recurse)
+-char *name;
+-int isuser, attr, recurse;
+-{
+- char *p = cfg_get_value(name, isuser, attr, recurse).pval;
+-
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_pvalue: returns %s",
+- p ? p : "NULL");
+- return(p);
+-}
+-
+-/* For getting host values */
+-static VALUE
+-cfg_get_hvalue(name, attr)
+-char *name;
+-int attr;
+-{
+- HOST *host;
+- VALUE value;
+-
+- value.pval = NULL;
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_hvalue: name=%s attr=%s ",
+- name, codestring(attr));
+-
+- /* find the host entry in hash table */
++const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+- host = (HOST *) hash_lookup( hosttable, name);
+-
+- if (!host) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_hvalue: no host named %s", name);
+- return (value);
+- }
+-
+- /* found the entry. Lookup value from attr=value */
+- value = get_hvalue(host, attr);
+-
+- if (value.pval) {
+- return (value);
+- }
+- /* No any value for this host */
+- value.pval = NULL;
+- return (value);
+-}
+-
+-/* Wrappers for cfg_get_hvalue */
+-char *
+-cfg_get_phvalue(name, attr)
+-char *name;
+-int attr;
++const char *
++cfg_get_pvalue(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
+ {
+- char *p = cfg_get_hvalue(name, attr).pval;
++ char *p = cfg_get_value(type, name, attr, recurse).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_phvalue: returns %s",
++ report(LOG_DEBUG, "cfg_get_pvalue: returns %s",
+ p ? p : "NULL");
+ return(p);
+ }
+
+-/*
+- Read the config file and do some basic sanity checking on
+- it. Return 1 if we find any errors. */
++/* Read the config file and do some basic sanity checking on
++ * it. Return 1 if we find any errors.
++ */
++int cfg_read_config TAC_ARGS((const char *cfile));
+
++int
+ cfg_read_config(cfile)
+-char *cfile;
++const char *cfile;
+ {
+ sym_line = 1;
+
+@@ -1679,7 +2244,17 @@ char *cfile;
+ return (1);
+ }
+
+- if (circularity_check()) {
++ if (0
++ || enlist_entity_connect()
++ || expr_sink_commit()
++ /* circularity is allowed in the new fully-recursive algorithm */
++ || (!algorithm_recursive && circularity_check())
++ ) {
++ fclose(cf);
++ return (1);
++ }
++ if (!WHEN_EXPR_ROOT_EMPTY() || when_expr_dungeon) {
++ report(LOG_ERR, "Some 'when' expression found still pushed on stack");
+ fclose(cf);
+ return (1);
+ }
+@@ -1688,346 +2263,443 @@ char *cfile;
+ return (0);
+ }
+
+-/* return 1 if user exists, 0 otherwise */
++/* return 1 if user exists, 0 otherwise
++ */
++int cfg_user_exists TAC_ARGS((const char *username));
++
+ int
+ cfg_user_exists(username)
+-char *username;
++const char *username;
+ {
+- USER *user = (USER *) hash_lookup(usertable, username);
+-
+- return (user != NULL);
++ return (NULL != hash_lookup(usertable, username));
+ }
+
+ /* return expiry string of user. If none, try groups she is a member
+- on, and so on, recursively if recurse is non-zero */
+-char *
+-cfg_get_expires(username, recurse)
+-char *username;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_expires TAC_ARGS((const char *username, int recurse));
+
++const char *
++cfg_get_expires(username, recurse)
++const char *username;
++int recurse;
+ {
+- return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse));
++ return (cfg_get_pvalue(S_user, username, S_expires, recurse));
+ }
+
+ /* return time string of user. If none, try groups she is a member
+- on, and so on, recursively if recurse is non-zero */
+-char *
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse));
++
++const char *
+ cfg_get_timestamp(username, recurse)
+-char *username;
++const char *username;
++int recurse;
+ {
+- return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse));
++ return (cfg_get_pvalue(S_user, username, S_time, recurse));
+ }
+
+-
+ /* return password string of user. If none, try groups she is a member
+- on, and so on, recursively if recurse is non-zero */
+-char *
+-cfg_get_login_secret(user, recurse)
+-char *user;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_login_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse));
++ return (cfg_get_pvalue(S_user, user, S_login, recurse));
+ }
+
+ /* return value of the nopasswd field. If none, try groups she is a member
+- on, and so on, recursively if recurse is non-zero */
++ * on, and so on, recursively if recurse is non-zero
++ */
++int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse));
++
+ int
+ cfg_get_user_nopasswd(user, recurse)
+- char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse));
++ return (cfg_get_intvalue(S_user, user, S_nopasswd, recurse));
+ }
+
+ /* return user's secret. If none, try groups she is a member
+- on, and so on, recursively if recurse is non-zero */
+-char *
+-cfg_get_arap_secret(user, recurse)
+-char *user;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_arap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_arap, recurse));
+ }
+
+-char *
+-cfg_get_chap_secret(user, recurse)
+-char *user;
++const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_chap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_chap, recurse));
+ }
+
+ #ifdef MSCHAP
+-char *
+-cfg_get_mschap_secret(user, recurse)
+-char *user;
+
++const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
++cfg_get_mschap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_mschap, recurse));
+ }
++
+ #endif /* MSCHAP */
+
+-char *
++const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
+ cfg_get_pap_secret(user, recurse)
+-char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_pap, recurse));
+ }
+
+-char *
++const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
+ cfg_get_opap_secret(user, recurse)
+-char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_opap, recurse));
+ }
+
+ /* return the global password for the user (or the group, etc.) */
+
+-char *
+-cfg_get_global_secret(user, recurse)
+-char *user;
++const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_global_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse));
++ return (cfg_get_pvalue(S_user, user, S_global, recurse));
+ }
+
+ #ifdef USE_PAM
++
+ /* Return a pointer to a node representing a PAM Service name */
+-char *
+-cfg_get_pam_service(user,recurse)
+-char *user;
+
++const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse));
++
++const char *
++cfg_get_pam_service(user, recurse)
++const char *user;
++int recurse;
+ {
+- char *cfg_passwd;
+- char *p;
++ const char *cfg_passwd;
++ const char *p;
+
+-cfg_passwd = cfg_get_pap_secret(user, recurse);
++ cfg_passwd = cfg_get_pap_secret(user, recurse);
+
+-if (!cfg_passwd) {
++ if (!cfg_passwd)
+ cfg_passwd = cfg_get_global_secret(user, recurse);
+-}
+
+-if (!cfg_passwd && !cfg_user_exists(user)) {
++ if (!cfg_passwd && !cfg_user_exists(user)) {
+ cfg_passwd = cfg_get_authen_default();
+ switch (cfg_get_authen_default_method()) {
++
+ case (S_pam):
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd);
+ return(cfg_passwd);
+ break;
++
+ default:
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I havent find any PAM Service!!");
+ return(NULL);/* Haven't any PAM Service!! */
+ }
+-}
++ }
+
+-p=tac_find_substring("pam ", cfg_passwd);
++ p = tac_find_substring("pam ", cfg_passwd);
+
+-if(p) { /* We find PAM services */
++ if(p) { /* We find PAM services */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I get PAM sevice:%s",p);
+-return (p);
+-}
++ return (p);
++ }
+
+-if (debug & DEBUG_AUTHOR_FLAG)
++ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "No any PAM Sevice");
+
+-return(NULL);
++ return(NULL);
+ }
+
+ #endif /* For PAM */
+
+
+-
+ /* Return a pointer to a node representing a given service
+ authorization, taking care of recursion issues correctly. Protocol
+- is only read if the type is N_svc_ppp. svcname is only read if type
++ is only read if the svctype is N_svc_ppp. svcname is only read if type
+ is N_svc.
+ */
+
+-NODE *
+-cfg_get_svc_node(username, type, protocol, svcname, recurse)
+-char *username;
+-int type;
+-char *protocol, *svcname;
+-int recurse;
+-{
+- USER *user, *group;
+- NODE *svc;
+-
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG,
+- "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d",
+- username,
+- cfg_nodestring(type),
+- protocol ? protocol : "",
+- svcname ? svcname : "",
+- recurse);
+-
+- /* find the user/group entry */
+- user = (USER *) hash_lookup(usertable, username);
++struct cfg_get_svc_node_param {
++ int svctype;
++ const char *protocol, *svcname;
++ NODE *node;
++ int retval;
++};
+
+- if (!user) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username);
+- return (NULL);
+- }
++static enum value_scan_func_result cfg_get_svc_node_func TAC_ARGS((ENTITY *entity, struct cfg_get_svc_node_param *param));
+
+- /* found the user entry. Find svc node */
+- for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
++static enum value_scan_func_result
++cfg_get_svc_node_func(entity, param /* func_data */)
++ENTITY *entity;
++struct cfg_get_svc_node_param *param;
++{
++ NODE *svc;
++ enum eval_result svc_default;
+
+- if (svc->type != type)
++ for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) {
++ if (svc->type != param->svctype)
+ continue;
+-
+- if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
++ if (param->svctype == N_svc_ppp && param->protocol && !STREQ(svc->value1, param->protocol))
+ continue;
+- }
+-
+- if (type == N_svc && !STREQ(svc->value1, svcname)) {
++ if (param->svctype == N_svc && param->svcname && !STREQ(svc->value1, param->svcname ))
++ continue;
++ if (expr_eval(svc->when) != ER_TRUE) /* expensive */
+ continue;
+- }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: found %s proto=%s svcname=%s",
+- cfg_nodestring(type),
+- protocol ? protocol : "",
+- svcname ? svcname : "");
++ cfg_nodestring(param->svctype),
++ param->protocol ? param->protocol : "",
++ param->svcname ? param->svcname : "");
+
+- return(svc);
++ param->node = svc;
++ param->retval = 1;
++ return (VSFR_FOUND);
+ }
+
+- if (!recurse) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+- return (NULL);
+- }
++ /* look at 'default service' settings */
++ svc_default = entity_svc_default(entity);
++ switch (svc_default) {
+
+- /* no matching node. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++ case ER_TRUE:
++ case ER_FALSE:
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG,
++ "cfg_get_svc_node: svc=%s protocol=%s svcname=%s forced %s by default service",
++ cfg_nodestring(param->svctype),
++ param->protocol ? param->protocol : "",
++ param->svcname ? param->svcname : "",
++ (svc_default == ER_TRUE ? "permit" : "deny"));
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s",
+- group->name);
++ param->retval = (svc_default == ER_TRUE);
++ return (VSFR_FOUND);
+
+- for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
++ default: /* shouldn't happen */
++ case ER_UNKNOWN:
++ return (VSFR_CONTINUE);
++ }
++ /* NOTREACHED */
++}
+
+- if (svc->type != type)
+- continue;
++int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep));
+
+- if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+- continue;
+- }
++int
++cfg_get_svc_node(username, svctype, protocol, svcname, recurse, nodep)
++const char *username;
++int svctype;
++const char *protocol;
++const char *svcname;
++int recurse;
++NODE **nodep;
++{
++ struct cfg_get_svc_node_param param;
++ enum value_scan_func_result vsfr;
+
+- if (type == N_svc && !STREQ(svc->value1, svcname)) {
+- continue;
+- }
++ param.svctype = svctype;
++ param.protocol = protocol;
++ param.svcname = svcname;
++ param.node = NULL;
++ param.retval = 0;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+- "cfg_get_svc_node: found %s proto=%s svcname=%s",
+- cfg_nodestring(type),
++ "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d",
++ username,
++ cfg_nodestring(svctype),
+ protocol ? protocol : "",
+- svcname ? svcname : "");
+-
+- return(svc);
+- }
++ svcname ? svcname : "",
++ recurse);
+
+- /* still nothing. Check containing group and so on */
++ vsfr = value_scan(S_user, username, recurse,
++ (value_scan_func_t) cfg_get_svc_node_func, ¶m /* func_data */);
++ if (nodep)
++ *nodep = param.node;
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
+- }
++ if (vsfr == VSFR_FOUND)
++ return (param.retval);
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+-
+- /* no matching svc node for this user or her containing groups */
+- return (NULL);
++ /* The service does not exist. Do the default */
++ return (cfg_no_user_permitted() ? 1 : 0);
+ }
+
+-/* Return a pointer to the node representing a set of command regexp
++/* Return a pointer to the node representing a set of command tac_regexp
+ matches for a user and command, handling recursion issues correctly */
+-NODE *
+-cfg_get_cmd_node(name, cmdname, recurse)
+-char *name, *cmdname;
+-int recurse;
+
++struct cfg_authorize_cmd_param {
++ const char *cmd;
++ const char *args;
++ enum eval_result result;
++};
++
++static enum value_scan_func_result cfg_authorize_cmd_func TAC_ARGS((ENTITY *entity, struct cfg_authorize_cmd_param *param));
++
++static enum value_scan_func_result
++cfg_authorize_cmd_func(entity, param /* func_data */)
++ENTITY *entity;
++struct cfg_authorize_cmd_param *param;
+ {
+- USER *user, *group;
+ NODE *svc;
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: name=%s cmdname=%s rec=%d",
+- name, cmdname, recurse);
++ for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) {
++ NODE *node;
+
+- /* find the user/group entry */
+- user = (USER *) hash_lookup(usertable, name);
++ if (svc->type != N_svc_cmd)
++ continue;
++ if (!STREQ(svc->value, param->cmd))
++ continue;
++ if (expr_eval(svc->when) != ER_TRUE) /* expensive */
++ continue;
+
+- if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name);
+- return (NULL);
++ report(LOG_DEBUG, "cfg_authorize_cmd: found cmd %s %s node",
++ param->cmd, cfg_nodestring(svc->type));
++
++ /* we have 'cmd ' point, now traverse through its 'permit'/'deny' pairs: */
++
++ for (node = svc->value1; node; node = node->next) {
++ int match;
++
++ if (expr_eval(node->when) != ER_TRUE) /* expensive */
++ continue;
++
++#ifdef WITH_INCLUDED_REGEX
++
++ match = tac_regexec((tac_regexp *) node->value1, param->args);
++
++#else /* WITH_INCLUDED_REGEX */
++
++ match = !regexec((const regex_t *) node->value1, param->args /* string */,
++ 0 /* nmatch */, NULL /* pmatch */, 0 /* eflags */);
++
++#endif /* WITH_INCLUDED_REGEX */
++
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
++ node->line, param->cmd,
++ node->type == N_permit ? "permit" : "deny",
++ (const char *) node->value, param->args, (match ? "" : "no "));
+ }
+- /* found the user entry. Find svc node */
+- svc = (NODE *) get_value(user, S_svc).pval;
+
+- while (svc) {
+- if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s %s node",
+- cmdname, cfg_nodestring(svc->type));
+- return (svc);
++ if (!match)
++ continue;
++
++ switch (node->type) {
++ case N_permit:
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_DEBUG, "%s %s permitted by line %d",
++ param->cmd, param->args, node->line);
+ }
+- svc = svc->next;
++ param->result = ER_TRUE;
++ return (VSFR_FOUND);
++ break;
++ case N_deny:
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_DEBUG, "%s %s denied by line %d",
++ param->cmd, param->args, node->line);
++ }
++ param->result = ER_FALSE;
++ return (VSFR_FOUND);
++ break;
++ default:
++ report(LOG_ERR, "INTERNAL: illegal configuration node: %s: %s %s",
++ session.peer, param->cmd, param->args);
++ param->result = ER_UNKNOWN; /* error */
++ return (VSFR_FOUND);
++ }
++ }
++ if (!algorithm_recursive) { /* compatibility mode: */
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s exists, but no args match, denied (as no 'authorization = recursive' found)",
++ param->cmd);
++ param->result = ER_FALSE; /* emulate last "deny .*" */
++ return (VSFR_FOUND);
+ }
+-
+- if (!recurse) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
+- return (NULL);
+ }
+- /* no matching node. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: recurse group = %s",
+- group->name);
++ /* look at 'default service' settings */
++ param->result = entity_svc_default(entity);
++ switch (param->result) {
+
+- svc = get_value(group, S_svc).pval;
++ case ER_TRUE:
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s does not exist, permitted by default", param->cmd);
++ return (VSFR_FOUND);
+
+- while (svc) {
+- if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s node %s",
+- cmdname, cfg_nodestring(svc->type));
+- return (svc);
+- }
+- svc = svc->next;
+- }
++ case ER_FALSE:
+
+- /* still nothing. Check containing group and so on */
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s does not exist, denied by default", param->cmd);
++ return (VSFR_FOUND);
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
++ default: /* shouldn't happen */
++ case ER_UNKNOWN:
++ return (VSFR_CONTINUE);
+ }
++ /* NOTREACHED */
++}
++
++enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args));
++
++enum eval_result
++cfg_authorize_cmd(username, cmd, args)
++const char *username;
++const char *cmd;
++const char *args;
++{
++ struct cfg_authorize_cmd_param param;
++
++ param.cmd = cmd;
++ param.args = args;
++ param.result = ER_UNKNOWN; /* error */
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
++ report(LOG_DEBUG, "cfg_authorize_cmd: name=%s cmdname=%s args=%s",
++ username, cmd, args);
+
+- /* no matching cmd node for this user or her containing groups */
+- return (NULL);
++ value_scan(S_user, username, TAC_PLUS_RECURSE,
++ (value_scan_func_t) cfg_authorize_cmd_func, ¶m /* func_data */);
++
++ if (param.result != ER_UNKNOWN)
++ return (param.result);
++
++ /* The command does not exist. Do the default */
++ return (cfg_no_user_permitted() ? ER_TRUE : ER_FALSE);
+ }
+
+ /* Return an array of character strings representing configured AV
+@@ -2039,6 +2711,8 @@ int recurse;
+ * Lastly, indicate what default permission was configured by setting
+ * denyp */
+
++char **cfg_get_svc_attrs TAC_ARGS((NODE *svcnode, int *denyp));
++
+ char **
+ cfg_get_svc_attrs(svcnode, denyp)
+ NODE *svcnode;
+@@ -2063,9 +2737,14 @@ int *denyp;
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next) {
+- char *arg = tac_strdup(node->value);
+- char *p = index(arg, '=');
++ char *arg;
++ char *p;
+
++ if (expr_eval(node->when) != ER_TRUE) /* expensive */
++ continue; /* ignore this node */
++
++ arg = tac_strdup(node->value);
++ p = index(arg, '=');
+ if (p && node->type == N_optarg)
+ *p = '*';
+ args[i++] = arg;
+@@ -2075,23 +2754,28 @@ int *denyp;
+ }
+
+
+-int
+-cfg_user_svc_default_is_permit(user)
+-char *user;
++static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity));
+
++static enum eval_result
++entity_svc_default(entity)
++ENTITY *entity;
+ {
+- int permit = cfg_get_intvalue(user, TAC_IS_USER, S_svc_dflt,
+- TAC_PLUS_RECURSE);
+-
+- switch (permit) {
+- default: /* default is deny */
+- case S_deny:
+- return (0);
++ switch (entity->svc_dflt) {
+ case S_permit:
+- return (1);
++ return (ER_TRUE);
++ case S_deny:
++ return (ER_FALSE);
++ case S_default:
++ case 0: /* not specified */
++ return (ER_UNKNOWN);
++ default:
++ report(LOG_ERR, "INTERNAL: invalid entity svc_dflt (%d)", entity->svc_dflt);
++ return (ER_UNKNOWN);
+ }
+ }
+
++int cfg_no_user_permitted TAC_ARGS((void));
++
+ int
+ cfg_no_user_permitted()
+ {
+@@ -2101,12 +2785,16 @@ cfg_no_user_permitted()
+ }
+
+
+-char *
++const char *cfg_get_authen_default TAC_ARGS((void));
++
++const char *
+ cfg_get_authen_default()
+ {
+ return (authen_default);
+ }
+
++int cfg_get_authen_default_method TAC_ARGS((void));
++
+ /* For describe authentication method(pam,file,db..etc) */
+ int
+ cfg_get_authen_default_method()
+@@ -2115,92 +2803,123 @@ cfg_get_authen_default_method()
+ }
+
+
+-/* Return 1 if this user has any ppp services configured. Used for
+- authorizing ppp/lcp requests */
+-int
+-cfg_ppp_is_configured(username, recurse)
+- char *username;
+- int recurse;
++/* Host entity management:
++ */
++
++const char *cfg_get_host_key TAC_ARGS((const char *host));
++
++/* For getting host key */
++const char *
++cfg_get_host_key(host)
++const char *host;
+ {
+- USER *user, *group;
+- NODE *svc;
++ return (cfg_get_pvalue(S_host, host, S_key, algorithm_recursive /* recurse */));
++}
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d",
+- username, recurse);
++static ENTITY *force_belong_entity TAC_ARGS((int type, const char *name));
++
++static ENTITY *
++force_belong_entity(type, name)
++int type;
++const char *name;
++{
++ ENTITY *entity = entity_lookup(type, name);
+
+- /* find the user/group entry */
+- user = (USER *) hash_lookup(usertable, username);
++ if (entity)
++ eval_force_belong_entity(entity);
+
+- if (!user) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s",
+- username);
+- return (0);
+- }
++ return (entity);
++}
+
+- /* found the user entry. Find svc node */
+- for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
++/* assumed existing initialized "session.peer*" */
+
+- if (svc->type != N_svc_ppp)
+- continue;
++static ENTITY *request_peer_addr;
++static ENTITY *request_peer;
++static ENTITY *request_DEFAULT_group;
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+- svc->value1);
++static void enlist_request_peer TAC_ARGS((const char *hostname, ENTITY **entityp));
+
+- return(1);
+- }
++static void
++enlist_request_peer(hostname, entityp)
++const char *hostname;
++ENTITY **entityp;
++{
++ *entityp = NULL;
++ if (!hostname)
++ return;
+
+- if (!recurse) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+- return (0);
+- }
++ *entityp = force_belong_entity(S_host, hostname);
++ if (*entityp && request_DEFAULT_group)
++ virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, *entityp /* child */);
++}
+
+- /* no matching node. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++/* Try to build the following scenery:
++ *
++ * host [=ER_TRUE]
++ * |
++ * +-- group
++ *
++ * host [=ER_TRUE]
++ * |
++ * +-- group
++ */
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: recurse group = %s",
+- group->name);
++void cfg_request_scan_begin TAC_ARGS((void));
+
+- for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
++void
++cfg_request_scan_begin()
++{
++ request_scan_begin();
+
+- if (svc->type != N_svc_ppp)
+- continue;
++ request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME);
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+- svc->value1);
++ if (session.peer_addr != session.peer)
++ enlist_request_peer(session.peer_addr, &request_peer_addr);
++ enlist_request_peer(session.peer, &request_peer);
++}
+
+- return(1);
+- }
++/* Try to build the following scenery:
++ *
++ * ( user username> | user ) [=ER_TRUE]
++ * |
++ * +-- host [=ER_TRUE]
++ * | |
++ * | +- group
++ * |
++ * +-- host [=ER_TRUE]
++ * | |
++ * | +-- group
++ * |
++ * +-- group
++ */
+
+- /* still nothing. Check containing group and so on */
++void cfg_request_identity TAC_ARGS((const struct identity *identity));
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
+- }
++void
++cfg_request_identity(identity)
++const struct identity *identity;
++{
++ ENTITY *user_entity,*request_DEFAULT_group;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
++ report(LOG_DEBUG, "cfg_request_identity: username=%s, NAS_name=%s, NAS_port=%s, NAC_address=%s, priv_lvl=%d",
++ identity->username, identity->NAS_name, identity->NAS_port, identity->NAC_address, identity->priv_lvl);
+
+- /* no PPP svc nodes for this user or her containing groups */
+- return (0);
+-}
++ user_entity = force_belong_entity(S_user, identity->username);
++ request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME);
+
+-/* For getting host key */
+-char *
+-cfg_get_host_key(host)
+-char *host;
+-{
+- return (cfg_get_phvalue(host, S_key));
+-}
++ if (!user_entity)
++ user_entity = force_belong_entity(S_user, DEFAULT_USERNAME);
++
++ request_scan_user_known = 1;
+
++ if (!user_entity)
++ return;
++
++ if (request_peer_addr)
++ virtual_enlist_entity_direct(request_peer_addr /* parent */, user_entity /* child */);
++ if (request_peer )
++ virtual_enlist_entity_direct(request_peer /* parent */, user_entity /* child */);
++ if (request_DEFAULT_group)
++ virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, user_entity /* child */);
++}
+diff --git a/cfgfile.h b/cfgfile.h
+new file mode 100644
+index 0000000..3669996
+--- /dev/null
++++ b/cfgfile.h
+@@ -0,0 +1,164 @@
++#ifndef CFGFILE_H
++#define CFGFILE_H 1
++
++#include "tac_plus.h"
++
++#include "utils.h"
++#include "cfgeval.h"
++
++
++/* Configurable:
++ */
++
++#define DEFAULT_USERNAME "DEFAULT"
++#define DEFAULT_GROUPNAME "DEFAULT"
++
++
++#define TAC_PLUS_RECURSE 1
++#define TAC_PLUS_NORECURSE 0
++
++/* Node types */
++
++#define N_arg 50
++#define N_optarg 51
++#define N_svc_exec 52
++#define N_svc_slip 53
++#define N_svc_ppp 54
++#define N_svc_arap 55
++#define N_svc_cmd 56
++#define N_permit 57
++#define N_deny 58
++#define N_svc 59
++
++typedef struct node NODE;
++
++/* A parse tree node */
++struct node {
++ int type; /* node type (arg, svc, proto) */
++ NODE *next; /* pointer to next node in chain */
++ void *value; /* node value */
++ void *value1; /* node value */
++ int dflt; /* default value for node */
++ int line; /* line number declared on */
++ struct expr *when; /* conditions needed to respect this NODE */
++};
++
++union v {
++ int intval;
++ void *pval;
++};
++
++typedef union v VALUE;
++
++/* A user, host or group definition
++
++ The first 2 fields (name and hash) are used by the hash table
++ routines to hash this structure into a table. Move them at your
++ peril
++*/
++
++struct entity {
++ char *name; /* username/groupname/hostname */
++ void *hash; /* hash table next pointer */
++ int line; /* line number defined on */
++ int type; /* set to S_user, S_host or S_group */
++
++ char *full_name; /* users full name */
++ char *login; /* Login password */
++ int nopasswd; /* user requires no password */
++ char *global; /* password to use if none set */
++ char *expires; /* expiration date */
++ char *arap; /* our arap secret */
++ char *pap; /* our pap secret */
++ char *opap; /* our outbound pap secret */
++ char *chap; /* our chap secret */
++#ifdef MSCHAP
++ char *mschap; /* our mschap secret */
++#endif /* MSCHAP */
++ char *msg; /* a message for this user */
++ char *before_author; /* command to run before authorization */
++ char *after_author; /* command to run after authorization */
++ char *key; /* host spesific key (N/A for S_user) */
++ int svc_dflt; /* default authorization behaviour for svc or
++ * cmd */
++ /* =S_permit, S_deny or S_default */
++ NODE *svcs; /* pointer to svc nodes */
++#ifdef MAXSESS
++ int maxsess; /* Max sessions/user */
++#endif /* MAXSESS */
++ char *time; /* Timestamp */
++
++ struct tac_list to_parent_membership_list; /* ordered list of memberships to groups owning us: */
++ struct tac_list to_child_membership_list; /* ordered list of memberships to entities in this group: */
++ unsigned to_child_membership_num; /* # of 'to_child_membership_list' items */
++
++ struct {
++ unsigned seq; /* corresponds to global request_scan_seq */
++ enum eval_result belongs; /* whether this ENTITY 'belongs' */
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global value_scan_seq */
++ unsigned seen:1;
++ struct membership *from; /* from which we got to this one or NULL */
++ } value_scan; /* cfg_get_value() scanning, many per request_scan */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ struct tac_list notify_expr_list; /* contains expr.u.waiting_expr_node */
++ /* may be from any of: eval_{want,solved,destroy}_entity_list: */
++ struct tac_list_node pending_entity_node; /* we are interested in this entity */
++ /* child memberships which are not yet check_eval-ed are NOT present here,
++ * although when check_eval-entity finishes, all will be added here.
++ * List refilling driven by check_eval_scan_entity(),
++ * although each unsolved_child_node is added in check_eval_scan_membership().
++ */
++ unsigned unsolved_to_child_membership_num; /* when 0, we know we are ER_FALSE */
++ struct membership *unsolved_to_child_membership_first;
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++};
++#define PENDING_ENTITY_NODE_TO_ENTITY(pending_entity_node_) \
++ (&TAC_MEMBER_STRUCT(ENTITY, (pending_entity_node_), eval_scan.pending_entity_node))
++
++
++struct identity;
++
++extern const char *cfg_nodestring TAC_ARGS((int type));
++extern void cfg_clean_config TAC_ARGS((void));
++extern int cfg_get_intvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
++extern const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
++extern int cfg_read_config TAC_ARGS((const char *cfile));
++extern int cfg_user_exists TAC_ARGS((const char *username));
++extern const char *cfg_get_expires TAC_ARGS((const char *username, int recurse));
++extern const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse));
++extern const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse));
++extern int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse));
++#ifdef MSCHAP
++extern const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse));
++#endif /* MSCHAP */
++extern const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse));
++#ifdef USE_PAM
++extern const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse));
++#endif /* PAM */
++extern int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep));
++extern char **cfg_get_svc_attrs TAC_ARGS((NODE *svcnode, int *denyp));
++extern int cfg_no_user_permitted TAC_ARGS((void));
++extern const char *cfg_get_authen_default TAC_ARGS((void));
++extern int cfg_get_authen_default_method TAC_ARGS((void));
++extern const char *cfg_get_host_key TAC_ARGS((const char *host));
++extern void cfg_request_scan_begin TAC_ARGS((void));
++extern void cfg_request_identity TAC_ARGS((const struct identity *identity));
++extern enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args));
++
++/* for use by cfgeval.c: */
++extern ENTITY *new_entity TAC_ARGS((int type, char *name, int line));
++extern const char *entity_type_to_string TAC_ARGS((int entity_type));
++extern void scan_invalidate_entities TAC_ARGS((enum invalidate_scan what));
++extern ENTITY *entity_lookup TAC_ARGS((int type, const char *name));
++
++
++#endif /* CFGFILE_H */
+diff --git a/choose_authen.c b/choose_authen.c
+index 9329b73..d6b3062 100644
+--- a/choose_authen.c
++++ b/choose_authen.c
+@@ -17,24 +17,47 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include "choose_authen.h"
+ #include "expire.h"
++#include "enable.h"
++#include "report.h"
++#include "cfgfile.h"
++#include "default_fn.h"
++#include "default_v0_fn.h"
++#include "sendauth.h"
++#include "sendpass.h"
++#include "packet.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
+
+-static int choose_login();
+-static int choose_sendpass();
+-static int choose_sendauth();
++#ifdef SKEY
++#include "skey_fn.h"
++#endif
+
+-int
++
++static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type));
++static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type));
++static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
++
++#if 0 /* unused */
++static int
+ get_minor_version()
+ {
+ return(session.version & ~TAC_PLUS_MAJOR_VER_MASK);
+ }
++#endif /* unused */
+
+ /*
+ * Choose an authentication function. Return CHOOSE_OK if chosen,
+ * CHOOSE_GETUSER if we need a username, CHOOSE_FAILED on failure
+ */
+
++int choose_authen TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ int
+ choose_authen(data, type)
+ struct authen_data *data;
+@@ -84,14 +107,16 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ /* Choose an authentication function for action == LOGIN, service != enable */
+ static int
+ choose_login(data, type)
+ struct authen_data *data;
+ struct authen_type *type;
+ {
+- char *name = data->NAS_id->username;
+- char *cfg_passwd;
++ const char *name = data->NAS_id->username;
++ const char *cfg_passwd;
+
+ switch(type->authen_type) {
+ case TAC_PLUS_AUTHEN_TYPE_ASCII:
+@@ -193,6 +218,8 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ static int
+ choose_sendauth(data, type)
+ struct authen_data *data;
+@@ -247,6 +274,8 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ /* Compatibility routine for (obsolete) minor version == 0 */
+ static int
+ choose_sendpass(data, type)
+@@ -291,4 +320,3 @@ struct authen_type *type;
+
+ return(CHOOSE_FAILED);
+ }
+-
+diff --git a/choose_authen.h b/choose_authen.h
+new file mode 100644
+index 0000000..1276494
+--- /dev/null
++++ b/choose_authen.h
+@@ -0,0 +1,63 @@
++#ifndef CHOOSE_AUTHEN_H
++#define CHOOSE_AUTHEN_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++/*
++ * This structure describes an authentication method.
++ * authen_name contains the name of the authentication method.
++ * authen_func is a pointer to the authentication function.
++ * authen_method numeric value of authentication method
++ */
++
++#define AUTHEN_NAME_SIZE 128
++
++struct authen_data;
++
++struct authen_type {
++ char authen_name[AUTHEN_NAME_SIZE];
++ int (*authen_func) TAC_ARGS((struct authen_data *data));
++ int authen_type;
++};
++
++/*
++ * The authen_data structure is the data structure for passing
++ * information to and from the authentication function
++ * (authen_type.authen_func).
++ */
++
++struct authen_data {
++ struct identity *NAS_id; /* user identity */
++ char *server_msg; /* null-terminated output msg */
++
++ int server_dlen; /* output data length */
++ unsigned char *server_data; /* output data */
++
++ char *client_msg; /* null-terminated input msg a user typed */
++
++ int client_dlen; /* input data length */
++ char *client_data; /* input data */
++
++ void *method_data; /* opaque private method data */
++ int action; /* what's to be done */
++ int service; /* calling service */
++ int status; /* Authen status */
++ int type; /* Authen type */
++ u_char flags; /* input & output flags fields */
++};
++
++/* return values for choose_authen(); */
++
++#define CHOOSE_FAILED -1 /* failed to choose an authentication function */
++#define CHOOSE_OK 0 /* successfully chose an authentication function */
++#define CHOOSE_GETUSER 1 /* need a username before choosing */
++#define CHOOSE_BADTYPE 2 /* Invalid preferred authen function specified */
++
++
++extern int choose_authen TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
++
++#endif /* CHOOSE_AUTHEN_H */
+diff --git a/configure.in b/configure.in
+index 6b3eb13..0b8fd72 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1,53 +1,101 @@
+ dnl This file writen by Devrim SERAL for tac_plus daemon
+
+ AC_INIT()
++dnl Check for Host information
++dnl AC_CANONICAL_HOST()
++AC_CANONICAL_SYSTEM()
++AM_INIT_AUTOMAKE(tac_plus, F4.0.3.alpha.8.gts4)
+
+ dnl Checks for programs.
+ AC_PROG_MAKE_SET
+ AC_PROG_CC
+
+-dnl Check for Host information
+-dnl AC_CANONICAL_HOST()
+-AC_CANONICAL_SYSTEM()
+-
+ case $host_os in
+ *linux-gnu)
+- OS="-DLINUX -DGLIBC"
++ AC_DEFINE(LINUX)
++ AC_DEFINE(GLIBC)
+ ;;
+ *solaris)
+- OS="-DSOLARIS"
++ AC_DEFINE(SOLARIS)
+ ;;
+ *freebsd)
+- OS="-DFREEBSD"
++ AC_DEFINE(FREEBSD)
+ ;;
+ *hpux)
+- OS="-DHPUX"
++ AC_DEFINE(HPUX)
+ ;;
+ *aix)
+- OS="-DAIX"
++ AC_DEFINE(AIX)
++ AC_MSG_WARN([See /usr/lpp/bos/bsdport on your system for details of how to define bsdcc])
++ # CC="bsdcc"
++ ;;
++ *mips)
++ AC_DEFINE(MIPS)
+ ;;
+ *)
+ ;;
+ esac
+
++dnl Devrim Added
++AM_CONFIG_HEADER(config.h)
++AM_MAINTAINER_MODE
++
++if test "x$USE_MAINTAINER_MODE" = "xyes"; then
++ AC_DEFINE(MAINTAINER_MODE)
++fi
++
++if test "x$USE_MAINTAINER_MODE" = "xyes" -a "x$GCC" = "xyes"; then
++ CFLAGS="$CFLAGS -ggdb3 -Wall -Wstrict-prototypes -pedantic -Wsign-compare"
++fi
++
++# Set these options as otherwise some autoconf tests give different results:
++final_CFLAGS="$CFLAGS"
++CFLAGS="$CFLAGS -D_XOPEN_SOURCE=1 -D_XOPEN_SOURCE_EXTENDED=1 -D_BSD_SOURCE=1 -D_OSF_SOURCE=1 -D__EXTENSIONS__=1"
++
++COND_USE=""
++AC_SUBST(COND_USE)
++conf_LDFLAGS=""
++AC_SUBST(conf_LDFLAGS)
++conf_LDADD=""
++AC_SUBST(conf_LDADD)
++
++
+ dnl Checks for libraries.
+ dnl Replace `main' with a function in -lnsl:
+-AC_CHECK_LIB(nsl, main)
++AC_CHECK_LIB(nsl, main, [ conf_LDADD="-lnsl $conf_LDADD" ] )
+ dnl Replace `main' with a function in -log:
+-AC_CHECK_LIB(og, main)
+-dnl Replace `main' with a function in -lldap:
+-AC_CHECK_LIB(ldap, main)
+-dnl Replace `main' with a function in -llber:
+-AC_CHECK_LIB(lber, main)
++AC_CHECK_LIB(og, main, [ conf_LDADD="-log $conf_LDADD" ] )
+ dnl Replace `main' with a function in -lsocket:
+-AC_CHECK_LIB(socket, main)
++AC_CHECK_LIB(socket, main, [ conf_LDADD="-lsocket $conf_LDADD" ] )
+ dnl Check for Crypt function
++dnl Never use CONF_LDADD here as it is used also for "generate_passwd"
+ AC_CHECK_LIB(crypt, crypt)
+ AC_CHECK_LIB(c,printf)
+
++dnl Checks for header files.
++AC_HEADER_STDC
++AC_CHECK_HEADERS(fcntl.h malloc.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h sys/syslog.h unistd.h regex.h sys/param.h)
++AC_CHECK_HEADERS(shadow.h,[
++ if test -f /etc/shadow ; then
++ AC_DEFINE(SHADOW_PASSWORDS)
++ fi
++ ],)
++dnl Checks for typedefs, structures, and compiler characteristics.
++AC_C_CONST
++AC_HEADER_TIME
++
++dnl Checks for library functions.
++AC_PROG_GCC_TRADITIONAL
++AC_FUNC_SETPGRP
++AC_TYPE_SIGNAL
++AC_FUNC_VPRINTF
++AC_FUNC_WAIT3
++AC_TYPE_SIZE_T
++AC_CHECK_FUNCS(select socket strcspn strdup strtol siginterrupt)
++AC_CHECK_SIZEOF(unsigned short,2)
++AC_CHECK_SIZEOF(unsigned int,4)
++AC_CHECK_SIZEOF(unsigned long,4)
+
+-dnl Devrim Added
+-AC_CONFIG_HEADER(config.h)
+
+ dnl For PAM support
+ AC_MSG_CHECKING(for PAM support:)
+@@ -55,9 +103,10 @@ echo
+ AC_ARG_WITH(pam,
+ [ --with-pam With PAM Support ],,)
+ if test "x$with_pam" = "xyes";then
+- AC_CHECK_LIB(dl, dlopen)
+- AC_CHECK_LIB(pam, pam_start)
+- DEFINES="-DUSE_PAM $DEFINES";
++ AC_CHECK_LIB(dl, dlopen, [ conf_LDADD="-ldl $conf_LDADD" ] )
++ AC_CHECK_LIB(pam, pam_start, [ conf_LDADD="-lpam $conf_LDADD" ] )
++ AC_DEFINE(USE_PAM)
++ COND_USE="$COND_USE "'$(cond_USE_PAM)'
+ AC_MSG_RESULT(Pam support... yes)
+ else
+ AC_MSG_RESULT(Pam support... no)
+@@ -70,10 +119,15 @@ AC_ARG_WITH(ldap,
+ [ --with-ldap With LDAP Support ],,)
+
+ if test "x$with_ldap" = "xyes";then
+- AC_CHECK_LIB(ldap, ldap_simple_bind_s)
+- AC_CHECK_LIB(ldap, ldap_init)
+-
+- DEFINES="-DUSE_LDAP $DEFINES"
++ dnl Replace `main' with a function in -llber:
++ AC_CHECK_LIB(lber, main, [ conf_LDADD="-llber $conf_LDADD"; liblber="-llber" ], [ liblber="" ] )
++ dnl Replace `main' with a function in -lldap:
++ AC_CHECK_LIB(ldap, ldap_simple_bind_s, [ conf_LDADD="-lldap $conf_LDADD" ],
++ [
++ AC_CHECK_LIB(ldap, ldap_init, [ conf_LDADD="-lldap $conf_LDADD" ],, $liblber)
++ ], $liblber )
++ AC_DEFINE(USE_LDAP)
++ COND_USE="$COND_USE "'$(cond_USE_LDAP)'
+ AC_MSG_RESULT(LDAP support... yes)
+ else
+ AC_MSG_RESULT(LDAP support... no)
+@@ -85,7 +139,10 @@ echo
+ AC_ARG_WITH(db,
+ [ --with-db For DB Support ],,)
+ if test "x$with_db" = "xyes";then
+- DB="$DB -DDB -DDB_NULL"
++ AC_DEFINE(DB)
++ AC_DEFINE(DB_NULL)
++ COND_USE="$COND_USE "'$(cond_DB)'
++ COND_USE="$COND_USE "'$(cond_DB_NULL)'
+ AC_MSG_RESULT(DB support... yes)
+ else
+ AC_MSG_RESULT(DB support... no)
+@@ -108,14 +165,15 @@ AC_ARG_WITH(mysql-prefix,
+
+ if test "x$with_mysql" = "xyes";then
+
+- LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $LDFLAGS"
+- LDFLAGS="-I$MYSQL_PREFIX/include/mysql $LDFLAGS"
++ conf_LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $conf_LDFLAGS"
++ CPPFLAGS="-I$MYSQL_PREFIX/include/mysql $CPPFLAGS"
+ AC_CHECK_LIB(mysqlclient, mysql_init,
+- LIBS="-lmysqlclient -lm $LIBS",
++ conf_LDADD="-lmysqlclient -lm $conf_LDADD",
+ AC_MSG_ERROR(*** couldn't find libmysqlclient),
+ -lm)
+
+- DB="$DB -DDB_MYSQL";
++ AC_DEFINE(DB_MYSQL)
++ COND_USE="$COND_USE "'$(cond_DB_MYSQL)'
+ AC_MSG_RESULT(Mysql support... yes)
+ else
+ AC_MSG_RESULT(Mysql support... no)
+@@ -140,13 +198,14 @@ AC_ARG_WITH(pgsql-prefix,
+
+ if test "x$with_pgsql" = "xyes";then
+
+- LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $LDFLAGS"
+- LDFLAGS="-I$PGSQL_PREFIX/include/pgsql $LDFLAGS"
++ conf_LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $conf_LDFLAGS"
++ CPPFLAGS="-I$PGSQL_PREFIX/include/pgsql $CPPFLAGS"
+ AC_CHECK_LIB(pq,PQconnectdb ,
+- LIBS="-lpq $LIBS",
++ conf_LDADD="-lpq $conf_LDADD",
+ AC_MSG_ERROR(*** couldn't find libpq))
+
+- DB="$DB -DDB_PGSQL";
++ AC_DEFINE(DB_PGSQL)
++ COND_USE="$COND_USE "'$(cond_DB_PGSQL)'
+ AC_MSG_RESULT(Pgsql support... yes)
+ else
+ AC_MSG_RESULT(Pgsql support... no)
+@@ -164,7 +223,8 @@ AC_ARG_WITH(tacgid,
+
+ if (test "x$with_tacuid" != "x" && test "x$with_tacgid" != "x" && test "x$with_tacuid" != "xyes" && test "x$with_tacgid" != "xyes");then
+
+- DEFINES="-DTACPLUS_USERID=$with_tacuid -DTACPLUS_GROUPID=$with_tacgid $DEFINES";
++ AC_DEFINE_UNQUOTED(TACPLUS_USERID, $with_tacuid)
++ AC_DEFINE_UNQUOTED(TACPLUS_GROUPID, $with_tacgid)
+ AC_MSG_RESULT(tacacs+ work with given user and group id)
+ fi
+
+@@ -173,8 +233,9 @@ AC_ARG_ENABLE(maxsess,
+ [ --enable-maxsess Enable maxsess feature ],
+ [
+ if test "$enableval" = "yes";then
+- DEFINES="-DMAXSESS $DEFINES";
++ AC_DEFINE(MAXSESS)
+ AC_MSG_RESULT(yes)
++ COND_USE="$COND_USE "'$(cond_MAXSESS)'
+ else
+ AC_MSG_RESULT(no)
+ fi
+@@ -184,16 +245,18 @@ fi
+ ])
+
+ dnl Enable tacacs.pid file directory
+-
+ AC_ARG_WITH(tacplus_pid,
+ [ --with-tacplus_pid=PREFIX Tac_plus pid file location [default=/var/run] ],
+- PIDFILE="-DTACPLUS_PIDFILE=\\\"$withval/tac_plus.pid\\\"",
+- PIDFILE="-DTACPLUS_PIDFILE=\\\"/var/run/tac_plus.pid\\\""
++ [ pidfile="$withval" ],
++ [ pidfile="" ]
+ )
++if test "x$pidfile" '!=' "x"; then
++ AC_DEFINE_UNQUOTED(TACPLUS_PIDFILE, "$pidfile/tac_plus.pid")
++fi
+
+ dnl For libwrap check
+-AC_MSG_CHECKING(whether to enable the libwrap feture)
+-
++AC_MSG_CHECKING(whether to enable the libwrap feature)
++cond=false
+ AC_ARG_WITH(libwrap,
+ [ --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support.],
+ [ case "$withval" in
+@@ -203,48 +266,221 @@ AC_ARG_WITH(libwrap,
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_CHECK_LIB(wrap, request_init, [
+- LIBS="-lwrap $LIBS"
+- DEFINES="-DTCPWRAPPER $DEFINES"])
++ conf_LDADD="-lwrap $conf_LDADD"
++ cond=true
++ ])
+ ;;
+ *)
+ AC_MSG_RESULT(yes)
+ if test -d "$withval"; then
+ LDFLAGS="-L$withval $LDFLAGS"
+- DEFINES="-DTCPWRAPPER $DEFINES"
+ fi
+ AC_TRY_LINK([ int allow_severity; int deny_severity; ],
+ [ hosts_access(); ],
+ [],
+ [ AC_MSG_ERROR(Could not find the $withval library. You must first install tcp_wrappers.) ])
++ cond=true
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+ )
++if $cond; then
++ AC_DEFINE(TCPWRAPPER)
++ COND_USE="$COND_USE "'$(cond_TCPWRAPPER)'
++fi
+
+-dnl insert defines to Makefile
+-AC_SUBST(DEFINES)
+-AC_SUBST(PIDFILE)
+-AC_SUBST(DB)
+-AC_SUBST(OS)
++dnl For SKEY check
++AC_MSG_CHECKING(whether to use SKEY security feature)
++cond=false
++AC_ARG_WITH(skey,
++[ --with-skey[=LIBPATH] Compile with SKEY support (also use -I in CPPFLAGS var).],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT(yes)
++ cond=true
++ ;;
++ *)
++ AC_MSG_RESULT(yes)
++ if test '!' -f "$withval";then
++ AC_MSG_ERROR([Cannot find $withval library file, you may wish to use LIBS variable instead.])
++ fi
++ conf_LDADD="$withval $conf_LDADD"
++ cond=true
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++if $cond; then
++ AC_DEFINE(SKEY)
++ COND_USE="$COND_USE "'$(cond_SKEY)'
++fi
+
+-dnl Checks for header files.
+-AC_HEADER_STDC
+-AC_CHECK_HEADERS(fcntl.h malloc.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h)
+-AC_CHECK_HEADERS(shadow.h,[
+- if test -f /etc/shadow ; then
+- AC_DEFINE(SHADOW_PASSWORDS)
++dnl For MSCHAP and also MSCHAP_DES
++AC_MSG_CHECKING(whether to compile with Microsoft CHAP)
++cond=false
++AC_ARG_WITH(mschap,
++[ --with-mschap[=des] Compile with Microsoft CHAP (optionally including MSCHAP_DES).],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT([yes, without DES])
++ cond=true
++ ;;
++ des)
++ AC_MSG_RESULT([yes, including DES])
++ AC_DEFINE(MSCHAP_DES)
++ cond=true
++ ;;
++ *)
++ AC_MSG_ERROR(Unknown --with-mschap argument $withval, use: no, yes or des)
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++if $cond; then
++ AC_DEFINE(MSCHAP)
++ COND_USE="$COND_USE "'$(cond_MSCHAP)'
++fi
++
++dnl For SunOS encryption compatibility
++dnl Never use CONF_LDADD here as it is used also for "generate_passwd"
++AC_MSG_CHECKING(whether to use SunOS encryption compatibility)
++cond=false
++AC_ARG_WITH(descrypt,
++[ --with-descrypt Be password encryption compatible with SunOS.],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT(yes)
++ LIBS="-ldescrypt $LIBS"
++ ;;
++ *)
++ AC_MSG_RESULT(yes - $withval)
++ LIBS="$withval $LIBS"
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++
++AC_ARG_WITH(efence,
++[ --with-efence compile with efence support (for debugging)],,[
++ if test "x$USE_MAINTAINER_MODE" = "xyes"; then
++ with_efence=auto
++ else
++ with_efence=no
+ fi
+- ],)
+-dnl Checks for typedefs, structures, and compiler characteristics.
+-AC_C_CONST
+-AC_HEADER_TIME
++])
++if test "$with_efence" != no; then
++ AC_CHECK_LIB(efence,malloc,,[
++ if test "$with_efence" = yes; then
++ AC_MSG_ERROR(Unable to find efence library.)
++ fi
++ ])
++fi
+
+-dnl Checks for library functions.
+-AC_PROG_GCC_TRADITIONAL
+-AC_FUNC_SETPGRP
+-AC_TYPE_SIGNAL
+-AC_FUNC_VPRINTF
+-AC_FUNC_WAIT3
+-AC_CHECK_FUNCS(regcomp select socket strcspn strdup strtol)
++dnl Check for type in sys/socket.h
++AC_MSG_CHECKING([for parameter type of 3rd accept() arg])
++AC_CACHE_VAL(tac_plus_cv_accept_type, [
++ check_ok=false
++ for type in socklen_t size_t int; do
++ AC_TRY_COMPILE([
++#include
++#include
++#if STDC_HEADERS
++#include
++#include
++#endif
++],[
++ return 0;}
++ int accept(int s, struct sockaddr *addr, ]$type[ *addrlen)
++ { return 0; }
++ int discarded_main() {
++],
++ [check_ok=true;break],continue)
++ done
++ if $check_ok
++ then
++ tac_plus_cv_accept_type=$type
++ else
++ tac_plus_cv_accept_type=no
++ fi
++ ])
++if test "x$tac_plus_cv_accept_type" = "xno"
++then
++ AC_DEFINE(socklen_t,int)
++ AC_MSG_RESULT([failed to detect, will try int])
++else
++ AC_MSG_RESULT($tac_plus_cv_accept_type)
++ if test "x$tac_plus_cv_accept_type" != "xsocklen_t"
++ then
++ AC_DEFINE_UNQUOTED(socklen_t,$tac_plus_cv_accept_type)
++ fi
++fi
++
++dnl Check for system regex (stolen from "mutt" package)
++AC_ARG_WITH(included-regex, [ --with-included-regex Use the included regex library ],
++ [tac_plus_cv_included_regex=yes],
++ [AC_CHECK_FUNCS(regcomp, tac_plus_cv_included_regex=no, tac_plus_cv_included_regex=yes)])
++
++if test $tac_plus_cv_included_regex = no ; then
++AC_CACHE_CHECK([whether your system's regexp library is completely broken],
++ [tac_plus_cv_included_regex_broken],
++ AC_TRY_RUN([
++#ifdef HAVE_UNISTD_H
++#include
++#endif
++#ifdef HAVE_REGEX_H
++#include
++#endif
++main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
++ tac_plus_cv_included_regex_broken=no, tac_plus_cv_included_regex_broken=yes, tac_plus_cv_included_regex_broken=yes))
++ if test $tac_plus_cv_included_regex_broken = yes ; then
++ echo "Using the included regex instead." >&AC_FD_MSG
++ tac_plus_cv_included_regex=yes
++ fi
++fi
++if test $tac_plus_cv_included_regex = yes; then
++ AC_DEFINE(WITH_INCLUDED_REGEX)
++ COND_USE="$COND_USE "'$(cond_WITH_INCLUDED_REGEX)'
++fi
++
++dnl Check for "struct passwd.pw_{age,comment}"
++dnl Stolen from Julianne Frances Haugh's login replacement.
++AC_CACHE_CHECK(for pw_age in struct passwd,
++tac_plus_cv_struct_passwd_pw_age, AC_TRY_COMPILE([#include ],
++[ struct passwd pw; pw.pw_age = "" ],
++tac_plus_cv_struct_passwd_pw_age=yes, tac_plus_cv_struct_passwd_pw_age=no))
++
++if test "$tac_plus_cv_struct_passwd_pw_age" = "yes"; then
++ AC_DEFINE(HAVE_PASSWD_PW_AGE)
++fi
++AC_CACHE_CHECK(for pw_comment in struct passwd,
++tac_plus_cv_struct_passwd_pw_comment, AC_TRY_COMPILE([#include ],
++[ struct passwd pw; pw.pw_comment = "" ],
++tac_plus_cv_struct_passwd_pw_comment=yes, tac_plus_cv_struct_passwd_pw_comment=no))
++
++if test "$tac_plus_cv_struct_passwd_pw_comment" = "yes"; then
++ AC_DEFINE(HAVE_PASSWD_PW_COMMENT)
++fi
++AC_CACHE_CHECK(for ut_host in struct utmp,
++tac_plus_cv_struct_utmp_ut_host, AC_TRY_COMPILE([#include ],
++[ struct utmp ut; ut.ut_host = "" ],
++tac_plus_cv_struct_utmp_ut_host=yes, tac_plus_cv_struct_utmp_ut_host=no))
++
++if test "$tac_plus_cv_struct_utmp_ut_host" = "yes"; then
++ AC_DEFINE(HAVE_UTMP_UT_HOST)
++fi
++
++CFLAGS="$final_CFLAGS"
+
+-AC_OUTPUT(Makefile,echo timestamp > stamp-h)
++AC_OUTPUT([
++ Makefile
++ tac_plus.spec
++ ])
+diff --git a/convert.pl b/convert.pl
+index eb0b5c2..83cbdcc 100755
+--- a/convert.pl
++++ b/convert.pl
+@@ -141,6 +141,3 @@ exit 0 if ($acl_valid);
+ foreach $group (keys %groups) {
+ print "group = $group { }\n";
+ }
+-
+-
+-
+diff --git a/db.c b/db.c
+index 9cbbbe1..b5c156c 100644
+--- a/db.c
++++ b/db.c
+@@ -44,18 +44,47 @@
+ devrim(devrim@gazi.edu.tr)
+ */
+
+-#if defined(DB)
+-#include
++
+ #include "tac_plus.h"
++
++#ifdef DB
++
++#include
++#include
++#include
++
++#include "db.h"
++#include "report.h"
++#include "do_acct.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++#include "utils.h"
++
++
++#ifdef DB_MYSQL
++#include "db_mysql.h"
++#endif
++#ifdef DB_NULL
++#include "db_null.h"
++#endif
++#ifdef DB_PGSQL
++#include "db_pgsql.h"
++#endif
++
++
++static int check_db_type TAC_ARGS((char *db_type));
++
++
+ /* The databases recognized by this function */
+ #define DEFINED_DB {"null","mysql","pgsql"}
+
+-char *find_attr_value();
++int db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
+
+ int
+ db_verify(user, users_passwd, str_conn)
+-char *user, *users_passwd; /* Username and gived password */
+-char *str_conn; /* String connection to database */
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *str_conn; /* string connection to database */
+ {
+ char *buffer;
+ char *db_pref, *db_user, *db_password;
+@@ -65,11 +94,7 @@ char *str_conn; /* String connection to database */
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify %s by database at %s", user, str_conn);
+
+- buffer = db_pref = (char *)malloc( strlen(str_conn) + 1 );
+- if( buffer == NULL ){
+- report(LOG_DEBUG, "Error allocation memory");
+- return(0);
+- }
++ buffer = db_pref = (char *) tac_malloc( strlen(str_conn) + 1 );
+
+ strcpy( buffer, str_conn );
+
+@@ -216,6 +241,8 @@ char *str_conn; /* String connection to database */
+ }
+
+
++int db_acct TAC_ARGS((struct acct_rec *rec));
++
+ /* Db accounting routine */
+ int
+ db_acct(rec)
+@@ -227,12 +254,7 @@ struct acct_rec *rec;
+ char *a_username,*s_name,*c_name,*elapsed_time,*bytes_in,*bytes_out;
+ int ret;
+
+- buffer = db_pref = (char *)malloc( strlen(session.db_acct) + 1 );
+-
+- if( buffer == NULL ){
+- report(LOG_DEBUG, "Error allocation memory");
+- return(0);
+- }
++ buffer = db_pref = (char *) tac_malloc( strlen(session.db_acct) + 1 );
+
+ strcpy( buffer, session.db_acct);
+
+@@ -384,8 +406,10 @@ struct acct_rec *rec;
+
+ }
+
++static int check_db_type TAC_ARGS((char *db_type));
++
+ /* For checking DB type */
+-int
++static int
+ check_db_type(db_type)
+ char *db_type;
+ {
+@@ -400,4 +424,9 @@ for (i=0; dbp[i] ; i++ ) {
+ }
+ return ret;
+ }
++
++#else /* DB */
++
++TAC_SOURCEFILE_EMPTY
++
+ #endif /* DB */
+diff --git a/db.h b/db.h
+new file mode 100644
+index 0000000..dfc0c23
+--- /dev/null
++++ b/db.h
+@@ -0,0 +1,17 @@
++#ifndef DB_H
++#define DB_H 1
++
++#include "tac_plus.h"
++
++#ifdef DB
++
++
++struct acct_rec;
++
++extern int db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
++extern int db_acct TAC_ARGS((struct acct_rec *rec));
++
++
++#endif /* DB */
++
++#endif /* DB_H */
+diff --git a/db_mysql.c b/db_mysql.c
+index 8aef6d4..4c09f48 100644
+--- a/db_mysql.c
++++ b/db_mysql.c
+@@ -1,88 +1,88 @@
+-#if defined(DB_MYSQL) && defined(DB)
+-
+ /*
+-Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+-*/
++ * Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
++ */
++
+
+ #include "tac_plus.h"
++
++#if defined(DB_MYSQL) && defined(DB)
++
+ #include
+-#include "mysql.h"
++#include
++#include
++
++#include "db_mysql.h"
++#include "report.h"
++#include "pwlib.h"
++#include "main.h"
++#include "utils.h"
++
++
+ #define SQLCMDL 1024
+ #define AUTHSQL "SELECT %s FROM %s WHERE %s=\"%s\""
+ #define ACCTSQL "INSERT INTO %s (usern,s_name,c_name,elapsed_time,bytes_in,bytes_out,fin_t) VALUES (\"%s\",\"%s\",\"%s\",%s,%s,%s,NOW())"
+
+-MYSQL mysqldb;
+-MYSQL_RES *res;
+-MYSQL_ROW row;
+-MYSQL_FIELD *table_field;
+-
+-int mysql_db_verify(user, users_passwd, db_user, db_password,
+- db_hostname,db_name, db_table, dbfield_name, dbfield_passwd)
++static MYSQL mysqldb;
++static MYSQL_RES *res;
++static MYSQL_ROW row;
+
+
+-char *user, *users_passwd; /* Username and gived password */
+-char *db_user; /* db's parameters */
+-char *db_password;
+-char *db_hostname;
+-char *db_name;
+-char *db_table;
+-char *dbfield_name;
+-char *dbfield_passwd;
++int mysql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
+
++int mysql_db_verify(user, users_passwd, db_user, db_password,
++ db_hostname, db_name, db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
+-
+-char *real_passwd;
+-char *mysqlcmd;
+-int sql_len;
++ char *real_passwd;
++ char *mysqlcmd;
++ int sql_len;
+
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: verify %s", user);
+
+-/* Connect database server */
++ /* Connect database server */
+
+- if ( !( mysql_connect(&mysqldb,db_hostname,db_user,db_password) ) )
+- {
++ if ( !( mysql_connect(&mysqldb,db_hostname,db_user,db_password) ) ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot connect as %s", db_user);
+ return(0);
+ }
+
+-/*Select tacacs db */
++ /* Select tacacs db */
+
+- if ( mysql_select_db(&mysqldb,db_name) )
+- {
++ if ( mysql_select_db(&mysqldb,db_name) ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot find database named %s",db_name);
+ return(0);
+ }
+
+-/* Check select string length */
++ /* Check select string length */
+
+-sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
++ sql_len = strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+- if ( sql_len> SQLCMDL )
+- {
++ if ( sql_len> SQLCMDL ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Sql cmd exceed alowed limits");
+ return(0);
+ }
+
+-/* Prepare select string */
++ /* Prepare select string */
+
+-mysqlcmd=(char *) malloc(sql_len);
+-
+-if(mysqlcmd==NULL) {
+- if (debug & DEBUG_AUTHEN_FLAG)
+- report(LOG_ERR, "mysql_db_verify: mysqlcmd malloc error");
+- return(0);
+-}
++ mysqlcmd = (char *) tac_malloc(sql_len);
+
+-sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
++ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+-/* Query database */
++ /* Query database */
+
+- if (mysql_query(&mysqldb,mysqlcmd))
+- {
++ if (mysql_query(&mysqldb,mysqlcmd)) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database ");
+ free(mysqlcmd);
+@@ -91,32 +91,29 @@ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+ free(mysqlcmd);
+
+- if (!(res = mysql_store_result(&mysqldb)))
+- {
++ if (!(res = mysql_store_result(&mysqldb))) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot store result");
+ return(0);
+ }
+
+- if(!(row = mysql_fetch_row(res)))
+- {
++ if (!(row = mysql_fetch_row(res))) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot fetch row");
+ return(0);
+ }
+
+- if (strlen(row[0]) <=0 )
+- {
++ if (strlen(row[0]) <=0 ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: DB passwd entry is NULL");
+ return(0);
+ }
++
+ /* Allocate memory for real_passwd */
+- real_passwd=(char *) malloc(strlen(row[0])+1);
++ real_passwd=(char *) tac_malloc(strlen(row[0])+1);
+ strcpy(real_passwd,row[0]);
+
+- if (!mysql_eof(res))
+- {
++ if (!mysql_eof(res)) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Result not end!!");
+ return(0);
+@@ -125,7 +122,7 @@ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+ mysql_free_result(res);
+ mysql_close(&mysqldb);
+
+-if (debug & DEBUG_AUTHEN_FLAG)
++ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: verify password '%s' to DES encrypted string '%s'", users_passwd, real_passwd);
+
+ /* Try to verify the password */
+@@ -133,69 +130,65 @@ if (debug & DEBUG_AUTHEN_FLAG)
+ free(real_passwd);
+ return (0);
+ }
++
+ free(real_passwd);
+ return (1); /* Return 1 if verified, 0 otherwise. */
+ }
+
+-int
+-mysql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+
+-char *db_user; /* db's parameters */
+-char *db_password;
+-char *db_hostname;
+-char *db_name;
+-char *db_table;
+-char *s_name, *c_name,*a_username,*elapsed_time,*bytes_in,*bytes_out;
++int mysql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
+
++int
++mysql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const char *bytes_out;
+ {
++ char *mysqlcmd;
++ int sql_len;
+
+-char *mysqlcmd;
+-int sql_len;
+-
+-/* Connect database server */
++ /* Connect database server */
+
+- if (!(mysql_connect(&mysqldb,db_hostname,db_user,db_password)))
+- {
++ if (!(mysql_connect(&mysqldb,db_hostname,db_user,db_password))) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot connect as %s", db_user);
+ return(0);
+ }
+
+-/*Select tacacs db */
++ /*Select tacacs db */
+
+- if (mysql_select_db(&mysqldb,db_name))
+- {
++ if (mysql_select_db(&mysqldb,db_name)) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot find database named %s",db_name);
+ return(0);
+ }
+
+-/* Check buffer overflow for select string */
+-sql_len=strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
++ /* Check buffer overflow for select string */
++ sql_len = strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
+
+-if ( sql_len >SQLCMDL)
+- {
++ if ( sql_len >SQLCMDL) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: Sql cmd exceed alowed limits");
+ return(0);
+ }
+
+
+-/* Prepare select string */
+-mysqlcmd=(char *) malloc(sql_len);
++ /* Prepare select string */
++ mysqlcmd=(char *) tac_malloc(sql_len);
+
+-if(mysqlcmd==NULL) {
+- if (debug & DEBUG_ACCT_FLAG)
+- report(LOG_ERR, "mysql_db_acct: mysqlcmd malloc error");
+- return(0);
+-}
+-
+-sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
++ sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+-/* Query database */
++ /* Query database */
+
+- if (mysql_query(&mysqldb,mysqlcmd))
+- {
++ if (mysql_query(&mysqldb,mysqlcmd)) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database");
+ free(mysqlcmd);
+@@ -204,13 +197,18 @@ sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in
+
+ free(mysqlcmd);
+
+-/* Check if accounting is sucess */
+- if ( mysql_affected_rows( &mysqldb ) < 0 )
+- {
++ /* Check if accounting is sucess */
++ if ( mysql_affected_rows( &mysqldb ) < 0 ) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: Insert isn't sucess");
+ return(0);
+ }
++
+ return (1); /* Return 1 if verified, 0 otherwise. */
+ }
+-#endif
++
++#else /* defined(DB_MYSQL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_MYSQL) && defined(DB) */
+diff --git a/db_mysql.h b/db_mysql.h
+new file mode 100644
+index 0000000..7e806b6
+--- /dev/null
++++ b/db_mysql.h
+@@ -0,0 +1,15 @@
++#ifndef DB_MYSQL_H
++#define DB_MYSQL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_MYSQL) && defined(DB)
++
++
++extern int mysql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int mysql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_MYSQL) && defined(DB) */
++
++#endif /* DB_MYSQL_H */
+diff --git a/db_null.c b/db_null.c
+index 40252d8..9892ef0 100644
+--- a/db_null.c
++++ b/db_null.c
+@@ -6,22 +6,30 @@
+ ** DO_NOT_USE_THIS_FOR_WORK!
+ */
+
+-#if defined(DB_NULL) && defined(DB)
++
+ #include "tac_plus.h"
+
+-int null_db_verify(user, users_passwd, db_user, db_password, db_hostname,
+- db_table, dbfield_name, dbfield_passwd)
++#if defined(DB_NULL) && defined(DB)
++
++#include "db_null.h"
++#include "report.h"
++#include "main.h"
+
+-char *user, *users_passwd; /* Username and gived password */
+-char *db_user; /* db's parametr's */
+-char *db_password;
+-char *db_hostname;
+-char *db_table;
+-char *dbfield_name;
+-char *dbfield_passwd;
+
++int null_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++
++int null_db_verify(user, users_passwd, db_user, db_password, db_hostname,
++ db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parametr's */
++const char *db_password;
++const char *db_hostname;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
+-//report(LOG_DEBUG, "DB_NULL(%u) - ok", __LINE__);
++/* report(LOG_DEBUG, "DB_NULL(%u) - ok", __LINE__); */
+
+ /* Try to verify the password
+ Successful if username and password equal */
+@@ -36,22 +44,30 @@ char *dbfield_passwd;
+
+ /* Null Database Accounting */
+
++int null_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
+ int
+ null_db_acct(db_user, db_password, db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+-char *db_user; /* db's parametr's */
+-char *db_password;
+-char *db_hostname;
+-char *db_name;
+-char *db_table;
+-char *s_name;
+-char *c_name;
+-char *a_username;
+-char *elapsed_time;char *bytes_in;char *bytes_out;
++const char *db_user; /* db's parametr's */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const char *bytes_out;
+ {
+-report(LOG_INFO,"Db accounting user=%s pass=%s host=%s
+-db_name=%s table=%s servern=%s clientn=%s username=%s et=%s bi=%s bo=%s",db_user,db_password,db_hostname,
+-db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
+-return (1);
++ report(LOG_INFO,"Db accounting user=%s pass=%s host=%s \
++ db_name=%s table=%s servern=%s clientn=%s username=%s et=%s bi=%s bo=%s",db_user,db_password,db_hostname,
++ db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
++ return (1);
+ }
+-#endif
+
++#else /* defined(DB_NULL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_NULL) && defined(DB) */
+diff --git a/db_null.h b/db_null.h
+new file mode 100644
+index 0000000..4e82047
+--- /dev/null
++++ b/db_null.h
+@@ -0,0 +1,15 @@
++#ifndef DB_NULL_H
++#define DB_NULL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_NULL) && defined(DB)
++
++
++extern int null_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int null_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_NULL) && defined(DB) */
++
++#endif /* DB_NULL_H */
+diff --git a/db_pgsql.c b/db_pgsql.c
+index 22725fe..321133c 100644
+--- a/db_pgsql.c
++++ b/db_pgsql.c
+@@ -1,5 +1,3 @@
+-#if defined(DB_PGSQL) && defined(DB)
+-
+ /*
+ Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+ For PostgreSQL Authentication And Accounting
+@@ -7,80 +5,87 @@ For PostgreSQL Authentication And Accounting
+ This program protected with GPL License.
+ */
+
++
+ #include "tac_plus.h"
++
++#if defined(DB_PGSQL) && defined(DB)
++
+ #include
+-#include "libpq-fe.h"
++#include
++#include
++#include
++
++#include "db_pgsql.h"
++#include "main.h"
++#include "report.h"
++#include "utils.h"
++#include "pwlib.h"
++
++
++static void exit_nicely TAC_ARGS((PGconn *cn, PGresult *r));
++
++
+ #define SQLCMDL 1024
+ #define PWLEN 13
+ #define AUTHSQL "SELECT %s FROM %s WHERE %s='%s'"
+ #define ACCTSQL "INSERT INTO %s (usern,s_name,c_name,elapsed_time,bytes_in,bytes_out,fin_t) VALUES ('%s','%s','%s',%s,%s,%s,NOW())"
+
+-PGconn *conn;
+-PGresult *res;
+-
+-int pgsql_db_verify(user, users_passwd, db_user, db_password,
+- db_hostname,db_name, db_table, dbfield_name, dbfield_passwd)
++static PGconn *conn;
++static PGresult *res;
+
+
+-char *user, *users_passwd; /* Username and gived password */
+-char *db_user; /* db's parameters */
+-char *db_password;
+-char *db_hostname;
+-char *db_name;
+-char *db_table;
+-char *dbfield_name;
+-char *dbfield_passwd;
++int pgsql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
+
++int pgsql_db_verify(user, users_passwd, db_user, db_password,
++ db_hostname, db_name, db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
++ char *real_passwd;
++ char *pgsqlcmd;
++ int sql_len;
++ int nrow;
+
+-char *real_passwd;
+-char *pgsqlcmd;
+-int sql_len;
+-int nrow;
+-
+-if (debug & DEBUG_AUTHEN_FLAG)
++ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: verify %s", user);
+
+-/* Connect database server */
++ /* Connect database server */
+
+-conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
++ conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+-if ( PQstatus(conn) == CONNECTION_BAD )
+-{
++ if ( PQstatus(conn) == CONNECTION_BAD ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Connection to database %s failed", db_name);
+ return(0);
+-}
++ }
+
+-/* Check select string length */
++ /* Check select string length */
+
+-sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
++ sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+-if ( sql_len> SQLCMDL )
+-{
++ if ( sql_len> SQLCMDL ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Sql cmd exceed alowed limits");
+ return(0);
+-}
+-
+-/* Prepare select string */
++ }
+
+-pgsqlcmd=(char *) malloc(sql_len);
++ /* Prepare select string */
+
+-if(pgsqlcmd==NULL)
+-{
+- if (debug & DEBUG_AUTHEN_FLAG)
+- report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+- return(0);
+-}
++ pgsqlcmd=(char *) tac_malloc(sql_len);
+
+-sprintf(pgsqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
++ sprintf(pgsqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+-/* Query database */
+-res=PQexec(conn,pgsqlcmd);
++ /* Query database */
++ res=PQexec(conn,pgsqlcmd);
+
+-if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+-{
++ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "PGSQL: cannot query database ");
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+@@ -88,119 +93,109 @@ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ free(pgsqlcmd);
+ exit_nicely(conn,res);
+ return(0);
+-}
++ }
+
+-free(pgsqlcmd);
++ free(pgsqlcmd);
+
+-if( nrow=PQntuples(res)!=1)
+-{
++ if ( (nrow=PQntuples(res)) !=1 ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Have we got more than one password!!");
+ exit_nicely(conn,res);
+ return(0);
+-}
++ }
+
+-if ( PQgetisnull(res,0,PQfnumber(res,dbfield_passwd)) )
+-{
++ if ( PQgetisnull(res,0,PQfnumber(res,dbfield_passwd)) ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: DB passwd entry is NULL");
+ exit_nicely(conn,res);
+ return(0);
+-}
++ }
+
+ /* Allocate memory for real_passwd */
+- real_passwd=(char *) malloc(PWLEN+1);
++ real_passwd=(char *) tac_malloc(PWLEN+1);
+ strncpy(real_passwd,PQgetvalue(res,0,PQfnumber(res,dbfield_passwd)),PWLEN);
+ real_passwd[PWLEN]='\0';
+
+-exit_nicely(conn,res);
++ exit_nicely(conn,res);
+
+-if (debug & DEBUG_AUTHEN_FLAG)
++ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: verify password '%s' to DES encrypted string '%s'", users_passwd, real_passwd);
+
+ /* Try to verify the password */
+ if (!des_verify(users_passwd, real_passwd))
+- {
+ return (0);
+- }
+
+ return (1); /* Return 1 if verified, 0 otherwise. */
+ }
+
+ /* PGSQL ACCOUNTING function */
+
+-int pgsql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+-
+-char *db_user; /* db's parameters */
+-char *db_password;
+-char *db_hostname;
+-char *db_name;
+-char *db_table;
+-char *s_name, *c_name,*a_username,*elapsed_time,*bytes_in,*bytes_out;
++int pgsql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
+
++int pgsql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const char *bytes_out;
+ {
+-
+-char *pgsqlcmd;
+-int sql_len;
++ char *pgsqlcmd;
++ int sql_len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Accounting for %s begin", a_username);
+
+-/* Connect database server */
++ /* Connect database server */
+
+-conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
++ conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+-if ( PQstatus(conn) == CONNECTION_BAD )
+-{
++ if ( PQstatus(conn) == CONNECTION_BAD ) {
+ if (debug & DEBUG_ACCT_FLAG) {
+ report(LOG_DEBUG, "PGSQL: Connection to database %s failed", db_name);
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+ }
+ return(0);
+-}
++ }
+
+-/* Check select string length */
++ /* Check select string length */
+
+-sql_len=strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
++ sql_len=strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
+
+-if ( sql_len> SQLCMDL )
+-{
++ if ( sql_len> SQLCMDL ) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Sql cmd exceed alowed limits");
+ return(0);
+-}
+-
+-/* Prepare select string */
++ }
+
+-pgsqlcmd=(char *) malloc(sql_len);
++ /* Prepare select string */
+
+-if(pgsqlcmd==NULL)
+-{
+-if (debug & DEBUG_ACCT_FLAG)
+- report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+- return(0);
+-}
++ pgsqlcmd=(char *) tac_malloc(sql_len);
+
+-sprintf(pgsqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
++ sprintf(pgsqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+-/* Query database */
+-res=PQexec(conn,pgsqlcmd);
++ /* Query database */
++ res=PQexec(conn,pgsqlcmd);
+
+-if (!res || PQresultStatus(res) != PGRES_COMMAND_OK )
+-{
++ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK ) {
+ if (debug & DEBUG_ACCT_FLAG) {
+ report(LOG_DEBUG, "PGSQL: cannot establish database query");
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+-}
++ }
+ free(pgsqlcmd);
+ exit_nicely(conn,res);
+ return(0);
+-}
++ }
+
+-free(pgsqlcmd);
++ free(pgsqlcmd);
+
+-/* Flush all result and close connection */
+-exit_nicely(conn,res);
++ /* Flush all result and close connection */
++ exit_nicely(conn,res);
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Accounting for %s finished", a_username);
+@@ -208,11 +203,20 @@ exit_nicely(conn,res);
+ return (1); /* Return 1 if verified, 0 otherwise. */
+ }
+
+-int
+-exit_nicely(PGconn *cn,PGresult *r)
++
++static void exit_nicely TAC_ARGS((PGconn *cn, PGresult *r));
++
++static void
++exit_nicely(cn, r)
++PGconn *cn;
++PGresult *r;
+ {
+ PQclear(r);
+ PQfinish(cn);
+ }
+
+-#endif
++#else /* defined(DB_PGSQL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_PGSQL) && defined(DB) */
+diff --git a/db_pgsql.h b/db_pgsql.h
+new file mode 100644
+index 0000000..b966bfc
+--- /dev/null
++++ b/db_pgsql.h
+@@ -0,0 +1,15 @@
++#ifndef DB_PGSQL_H
++#define DB_PGSQL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_PGSQL) && defined(DB)
++
++
++extern int pgsql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int pgsql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_PGSQL) && defined(DB) */
++
++#endif /* DB_PGSQL_H */
+diff --git a/default_fn.c b/default_fn.c
+index f97e9e2..d6196cf 100644
+--- a/default_fn.c
++++ b/default_fn.c
+@@ -17,23 +17,50 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include
++#include
++#include
++
++#include "default_fn.h"
+ #include "expire.h"
+ #include "md5.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
+
+ #ifdef MSCHAP
+ #include "md4.h"
+ #include "mschap.h"
+-
+ #ifdef MSCHAP_DES
+ #include "arap_des.h"
+-#endif
++#endif /* MSCHAP_DES */
+ #endif /* MSCHAP */
+
+ #ifdef ARAP_DES
+ #include "arap_des.h"
+ #endif
+
++
++struct private_data;
++
++static void chap_verify TAC_ARGS((struct authen_data *data));
++static void arap_verify TAC_ARGS((struct authen_data *data));
++static void pap_verify TAC_ARGS((struct authen_data *data));
++static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
++
++#ifdef MSCHAP
++static void mschap_verify TAC_ARGS((struct authen_data *data));
++#endif
++
++
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+ #define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+@@ -44,13 +71,6 @@ struct private_data {
+ int state;
+ };
+
+-static void chap_verify();
+-#ifdef MSCHAP
+-static void mschap_verify();
+-#endif /* MSCHAP */
+-static void arap_verify();
+-static void pap_verify();
+-static void tac_login();
+
+ /*
+ * Default tacacs login authentication function. Wants a username
+@@ -67,6 +87,8 @@ static void tac_login();
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int default_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ default_fn(data)
+ struct authen_data *data;
+@@ -210,6 +232,8 @@ struct authen_data *data;
+ *
+ */
+
++static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
++
+ static void
+ tac_login(data, p)
+ struct authen_data *data;
+@@ -278,6 +302,8 @@ struct private_data *p;
+ * the START packet.
+ */
+
++static void pap_verify TAC_ARGS((struct authen_data *data));
++
+ static void
+ pap_verify(data)
+ struct authen_data *data;
+@@ -305,6 +331,8 @@ struct authen_data *data;
+ }
+
+
++static void chap_verify TAC_ARGS((struct authen_data *data));
++
+ /* Verify the challenge and id against the response by looking up the
+ * chap secret in the config file. Set data->status appropriately.
+ */
+@@ -312,8 +340,9 @@ static void
+ chap_verify(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal, digest[MD5_LEN];
+- char *exp_date, *p;
++ char *name, *chal, digest[MD5_LEN];
++ const char *secret;
++ const char *exp_date, *p;
+ u_char *mdp;
+ char id;
+ int chal_len, inlen;
+@@ -398,13 +427,15 @@ struct authen_data *data;
+ }
+
+
++static void pw_bitshift TAC_ARGS((char *pw));
++
+ /*
+ * Force the "parity" bit to zero on a password before passing it to
+ * des. This is not documented anywhere. (I believe forcing the parity
+ * to zero reduces the integrity of the encrypted keys but this is
+ * what Apple chose to do).
+ */
+-void
++static void
+ pw_bitshift(pw)
+ char *pw;
+ {
+@@ -423,12 +454,14 @@ char *pw;
+ }
+
+
++static void arap_verify TAC_ARGS((struct authen_data *data));
++
+ static void
+ arap_verify(data)
+ struct authen_data *data;
+ {
+ char nas_chal[8], r_chal[8], r_resp[8], secret[8];
+- char *name, *cfg_secret, *exp_date, *p;
++ const char *name, *cfg_secret, *exp_date, *p;
+
+ if (!(char) data->NAS_id->username[0]) {
+ report(LOG_ERR, "%s %s: no username for arap_verify",
+@@ -511,9 +544,12 @@ struct authen_data *data;
+ #ifdef MSCHAP
+
+ /* Following code is added for ms-chap */
++
++static void mschap_desencrypt TAC_ARGS((const char *clear, unsigned char *str, unsigned char *cypher));
++
+ static void
+ mschap_desencrypt(clear, str, cypher)
+-char *clear;
++const char *clear;
+ unsigned char *str;
+ unsigned char *cypher;
+ {
+@@ -576,19 +612,23 @@ unsigned char *cypher;
+ }
+
+
++static void mschap_deshash TAC_ARGS((unsigned char *clear, unsigned char *cypher));
++
+ static void
+ mschap_deshash(clear, cypher)
+-char *clear;
+-char *cypher;
++unsigned char *clear;
++unsigned char *cypher;
+ {
+ mschap_desencrypt(MSCHAP_KEY, clear, cypher);
+ }
+
+
++static void mschap_lmpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
++
+ static void
+ mschap_lmpasswordhash(password, passwordhash)
+-char *password;
+-char *passwordhash;
++const char *password;
++unsigned char *passwordhash;
+ {
+ unsigned char upassword[15];
+ int i = 0;
+@@ -604,13 +644,15 @@ char *passwordhash;
+ }
+
+
++static void mschap_challengeresponse TAC_ARGS((const char *challenge, unsigned char *passwordhash, unsigned char *response));
++
+ static void
+ mschap_challengeresponse(challenge, passwordhash, response)
+-char *challenge;
+-char *passwordhash;
+-char *response;
++const char *challenge;
++unsigned char *passwordhash;
++unsigned char *response;
+ {
+- char zpasswordhash[21];
++ unsigned char zpasswordhash[21];
+
+ memset(zpasswordhash, 0, 21);
+ memcpy(zpasswordhash, passwordhash, 16);
+@@ -621,22 +663,26 @@ char *response;
+ }
+
+
++void mschap_lmchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++
+ void
+ mschap_lmchallengeresponse(challenge, password, response)
+-char *challenge;
+-char *password;
+-char *response;
++const char *challenge;
++const char *password;
++unsigned char *response;
+ {
+- char passwordhash[16];
++ unsigned char passwordhash[16];
+
+ mschap_lmpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+ }
+
+
++static int mschap_unicode_len TAC_ARGS((unsigned char *password));
++
+ static int
+ mschap_unicode_len(password)
+-char *password;
++unsigned char *password;
+ {
+ int i;
+
+@@ -649,14 +695,16 @@ char *password;
+ }
+
+
++static void mschap_ntpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
++
+ static void
+ mschap_ntpasswordhash(password, passwordhash)
+-char *password;
+-char *passwordhash;
++const char *password;
++unsigned char *passwordhash;
+ {
+ MD4_CTX context;
+ int i;
+- char *cp;
++ const char *cp;
+ unsigned char unicode_password[512];
+
+ memset(unicode_password, 0, 512);
+@@ -676,15 +724,15 @@ char *passwordhash;
+ }
+
+
++void mschap_ntchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++
+ void
+-mschap_ntchallengeresponse(challenge,
+- password,
+- response)
+-char *challenge;
+-char *password;
+-char *response;
++mschap_ntchallengeresponse(challenge, password, response)
++const char *challenge;
++const char *password;
++unsigned char *response;
+ {
+- char passwordhash[16];
++ unsigned char passwordhash[16];
+
+ mschap_ntpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+@@ -694,16 +742,19 @@ char *response;
+ /* Verify the challenge and id against the response by looking up the
+ * ms-chap secret in the config file. Set data->status appropriately.
+ */
++
++static void mschap_verify TAC_ARGS((struct authen_data *data));
++
+ static void
+ mschap_verify(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal, *resp;
+- char *exp_date, *p;
++ const char *name, *secret, *chal, *resp;
++ const char *exp_date, *p;
+ char id;
+ int chal_len;
+- char lmresponse[24];
+- char ntresponse[24];
++ unsigned char lmresponse[24];
++ unsigned char ntresponse[24];
+ int bcmp_status;
+
+ if (!(char) data->NAS_id->username[0]) {
+diff --git a/default_fn.h b/default_fn.h
+new file mode 100644
+index 0000000..20e0e0f
+--- /dev/null
++++ b/default_fn.h
+@@ -0,0 +1,16 @@
++#ifndef DEFAULT_FN_H
++#define DEFAULT_FN_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int default_fn TAC_ARGS((struct authen_data *data));
++#ifdef MSCHAP
++extern void mschap_lmchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++extern void mschap_ntchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++#endif
++
++
++#endif /* DEFAULT_FN_H */
+diff --git a/default_v0_fn.c b/default_v0_fn.c
+index 96e0c1d..7c4c9cb 100644
+--- a/default_v0_fn.c
++++ b/default_v0_fn.c
+@@ -17,8 +17,23 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include
++#include
++
++#include "default_v0_fn.h"
+ #include "expire.h"
++#include "utils.h"
++#include "report.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
++#include "cfgfile.h"
++
+
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+@@ -45,6 +60,8 @@ struct private_data {
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int default_v0_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ default_v0_fn(data)
+ struct authen_data *data;
+@@ -185,4 +202,3 @@ struct authen_data *data;
+ return (1);
+ }
+ }
+-
+diff --git a/default_v0_fn.h b/default_v0_fn.h
+new file mode 100644
+index 0000000..1165ee4
+--- /dev/null
++++ b/default_v0_fn.h
+@@ -0,0 +1,12 @@
++#ifndef DEFAULT_V0_FN_H
++#define DEFAULT_V0_FN_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int default_v0_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* DEFAULT_V0_FN_H */
+diff --git a/do_acct.c b/do_acct.c
+index dee8eaa..287b0dc 100644
+--- a/do_acct.c
++++ b/do_acct.c
+@@ -17,18 +17,46 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include
++#include
++#include
++#include
++#include
++#ifdef HAVE_FCNTL_H
++#include
++#endif
++#include
++#include
++#ifdef HAVE_UNISTD_H
++#include
++#endif
++
++#include "do_acct.h"
++#include "report.h"
++#include "utils.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++
++
++char *wtmpfile = NULL; /* for wtmp file logging */
++
++
++static int wtmpfd = 0;
+ static int acctfd = 0;
+
+ /* Make a acct entry into the accounting file for accounting.
+ Return 1 on error */
+
++static int acct_write TAC_ARGS((char *string));
++
+ static int
+ acct_write(string)
+- char *string;
++char *string;
+ {
+- if (write(acctfd, string, strlen(string)) != strlen(string)) {
++ if ((unsigned long)write(acctfd, string, strlen(string)) != strlen(string)) {
+ report(LOG_ERR, "%s: couldn't write acct file %s %s",
+ session.peer,
+ session.acctfile, sys_errlist[errno]);
+@@ -41,11 +69,13 @@ acct_write(string)
+ return(0);
+ }
+
++static int acct_write_field TAC_ARGS((char *string));
++
+ /* Write a string or "unknown" into the accounting file.
+ Return 1 on error */
+ static int
+ acct_write_field(string)
+- char *string;
++char *string;
+ {
+ if (string && string[0]) {
+ if (acct_write(string))
+@@ -57,6 +87,8 @@ acct_write_field(string)
+ return(0);
+ }
+
++int do_acct TAC_ARGS((struct acct_rec *rec));
++
+ int
+ do_acct(rec)
+ struct acct_rec *rec;
+@@ -131,10 +163,12 @@ struct acct_rec *rec;
+ return (0);
+ }
+
+-int
++static int wtmp_entry TAC_ARGS((char *line, char *name, char *host, time_t utime));
++
++static int
+ wtmp_entry (line, name, host, utime)
+- char *line, *name, *host;
+- time_t utime;
++char *line, *name, *host;
++time_t utime;
+ {
+ struct utmp entry;
+
+@@ -152,7 +186,7 @@ wtmp_entry (line, name, host, utime)
+ strcpy(entry.ut_name, name);
+ else bcopy(name, entry.ut_name, sizeof entry.ut_name);
+
+-#ifndef SOLARIS
++#ifdef HAVE_UTMP_UT_HOST
+ if (strlen(host) < sizeof entry.ut_host)
+ strcpy(entry.ut_host, host);
+ else bcopy(host, entry.ut_host, sizeof entry.ut_host);
+@@ -180,16 +214,18 @@ wtmp_entry (line, name, host, utime)
+ close(wtmpfd);
+
+ if (debug & DEBUG_ACCT_FLAG) {
+- report(LOG_DEBUG, "wtmp: %s, %s %s %d", line, name, host, utime);
++ report(LOG_DEBUG, "wtmp: %s, %s %s %ld", line, name, host, (long)utime);
+ }
+
+ return(0);
+ }
+
++char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt));
++
+ char *
+ find_attr_value (attr, args, cnt)
+- char *attr, **args;
+- int cnt;
++char *attr, **args;
++int cnt;
+ {
+ int i;
+
+@@ -208,9 +244,11 @@ find_attr_value (attr, args, cnt)
+ return(NULL);
+ }
+
++int do_wtmp TAC_ARGS((struct acct_rec *rec));
++
+ int
+ do_wtmp(rec)
+- struct acct_rec *rec;
++struct acct_rec *rec;
+ {
+ time_t now = time(NULL);
+ char *service;
+diff --git a/do_acct.h b/do_acct.h
+new file mode 100644
+index 0000000..5e6e025
+--- /dev/null
++++ b/do_acct.h
+@@ -0,0 +1,33 @@
++#ifndef DO_ACCT_H
++#define DO_ACCT_H 1
++
++#include "tac_plus.h"
++
++
++/* An API accounting record structure */
++struct acct_rec {
++ int acct_type; /* start, stop, update */
++
++#define ACCT_TYPE_START 1
++#define ACCT_TYPE_STOP 2
++#define ACCT_TYPE_UPDATE 3
++
++ struct identity *identity;
++ int authen_method;
++ int authen_type;
++ int authen_service;
++ char *msg; /* output field */
++ char *admin_msg; /* output field */
++ int num_args;
++ char **args;
++};
++
++extern char *wtmpfile; /* for wtmp file logging */
++
++
++extern int do_acct TAC_ARGS((struct acct_rec *rec));
++extern char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt));
++extern int do_wtmp TAC_ARGS((struct acct_rec *rec));
++
++
++#endif /* DO_ACCT_H */
+diff --git a/do_author.c b/do_author.c
+index 574736f..3f23602 100644
+--- a/do_author.c
++++ b/do_author.c
+@@ -17,15 +17,39 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+-#include "regexp.h"
+
+-static int get_nas_svc();
+-static int authorize_cmd();
+-static int authorize_exec();
+-static int authorize_svc();
+-static void post_authorization();
+-static int pre_authorization();
++#include
++#include
++
++#include "do_author.h"
++#include "tac_regexp.h"
++#include "cfgfile.h"
++#include "report.h"
++#include "utils.h"
++#include "programs.h"
++#include "main.h"
++#include "parse.h"
++#include "cfgeval.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++#ifdef USE_PAM
++#include "tac_pam.h"
++#endif
++
++
++static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
++static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
++static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
++static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
++static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
++static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
++
++
++int do_author TAC_ARGS((struct author_data *data));
+
+ /* Return 0 is data->status is valid */
+ int
+@@ -37,7 +61,7 @@ struct author_data *data;
+ int svc;
+ char *cmd, *protocol, *svcname;
+ #ifdef USE_PAM
+- char *pam_service= NULL;
++ const char *pam_service= NULL;
+ #endif
+ protocol = NULL;
+
+@@ -48,7 +72,8 @@ struct author_data *data;
+
+ /* If this user doesn't exist in our configs, do the default */
+
+- if (!cfg_user_exists(username) && !cfg_user_exists(DEFAULT_USERNAME)) {
++ if (!cfg_user_exists(username)) {
++ if (!cfg_user_exists(DEFAULT_USERNAME)) {
+
+ if (cfg_no_user_permitted()) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+@@ -70,7 +95,8 @@ struct author_data *data;
+ return (0);
+ }
+
+- if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
++ /* assumed (cfg_user_exists(DEFAULT_USERNAME)): */
++
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'",
+ DEFAULT_USERNAME, username);
+@@ -127,19 +153,20 @@ struct author_data *data;
+ #ifdef USE_PAM
+ /* Check PAM Authorization */
+ switch (svc) {
++
+ case N_svc_ppp:
+ case N_svc_exec:
+-if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
++ if ((pam_service = cfg_get_pam_service(data->id->username, TAC_PLUS_RECURSE) )) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM Authorization begin for user %s",data->id->username);
+- if(tac_pam_authorization(data->id->username,data,pam_service))
+- {
++ if (tac_pam_authorization(data->id->username, data, pam_service)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM Authorization Fail");
+ return(0);
+ }
+-} /* Pam_service */
++ } /* Pam_service */
++
+ default:
+ break;
+ }
+@@ -181,15 +208,17 @@ if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
+ A return value of 1 means no further authorization is required
+ */
+
++static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
++
+ static int
+ pre_authorization(username, data)
+-char *username;
++const char *username;
+ struct author_data *data;
+ {
+ int status;
+ char **out_args;
+ int out_cnt, i;
+- char *cmd;
++ const char *cmd;
+ char error_str[255];
+ int error_len = 255;
+
+@@ -199,7 +228,7 @@ struct author_data *data;
+ /* If a before-authorization program exists, call it to see how to
+ proceed */
+
+- cmd = cfg_get_pvalue(username, TAC_IS_USER,
++ cmd = cfg_get_pvalue(S_user, username,
+ S_before, TAC_PLUS_RECURSE);
+ if (!cmd)
+ return(0);
+@@ -303,16 +332,17 @@ struct author_data *data;
+ change the authorization status by calling an external program.
+ */
+
++static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
++
+ static void
+ post_authorization(username, data)
+- char *username;
+- struct author_data *data;
++const char *username;
++struct author_data *data;
+ {
+ char **out_args;
+ int out_cnt, i;
+ int status;
+- char *after = cfg_get_pvalue(username, TAC_IS_USER,
+- S_after, TAC_PLUS_RECURSE);
++ const char *after = cfg_get_pvalue(S_user, username, S_after, TAC_PLUS_RECURSE);
+ if (!after)
+ return;
+
+@@ -378,6 +408,8 @@ post_authorization(username, data)
+ }
+
+
++static char *value TAC_ARGS((char *s));
++
+ /* Return a pointer to the value part of an attr=value string */
+ static char *
+ value(s)
+@@ -393,6 +425,8 @@ char *s;
+ /* Reassemble the command arguments as typed by the user, out of the
+ array of args we received. Return "" if there are no arguments */
+
++static char *assemble_args TAC_ARGS((struct author_data *data));
++
+ static char *
+ assemble_args(data)
+ struct author_data *data;
+@@ -445,30 +479,27 @@ struct author_data *data;
+ Otherwise, we return 1, indicating no further processing is
+ required for this request. */
+
++static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
++
+ static int
+ authorize_exec(user, data)
+-char *user;
++const char *user;
+ struct author_data *data;
+ {
+- NODE *svc;
+-
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec authorization request for %s", user);
+
+ /* Is an exec explicitly configured? If so, return 0 so we know to
+ process its attributes */
+
+- svc = cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE);
+- if (svc) {
++ if (cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+- report(LOG_DEBUG, "exec is explicitly permitted by line %d",
+- svc->line);
++ report(LOG_DEBUG, "exec is explicitly permitted");
+ return (0);
+ }
+
+ /* No exec is configured. Are any commands configured? */
+- svc = cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE);
+- if (svc) {
++ if (cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec permitted because commands are configured");
+
+@@ -478,18 +509,6 @@ struct author_data *data;
+ return (1);
+ }
+
+- /* No exec or commands configured. What's the default? */
+- if (cfg_user_svc_default_is_permit(user)) {
+-
+- if (debug & DEBUG_AUTHOR_FLAG)
+- report(LOG_DEBUG, "exec permitted by default");
+-
+- data->status = AUTHOR_STATUS_PASS_ADD;
+- data->output_args = NULL;
+- data->num_out_args = 0;
+- return (1);
+- }
+-
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec denied by default");
+
+@@ -501,16 +520,15 @@ struct author_data *data;
+ /* Is an exec command authorized per our database(s)?
+ Return 0 if status is valid */
+
++static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
++
+ static int
+ authorize_cmd(user, cmd, data)
+-char *user, *cmd;
++const char *user;
++const char *cmd;
+ struct author_data *data;
+ {
+- NODE *node;
+ char *args;
+- int match;
+-
+- args = assemble_args(data);
+
+ if (!cmd) {
+ data->status = AUTHOR_STATUS_ERROR;
+@@ -520,93 +538,41 @@ struct author_data *data;
+ return (0);
+ }
+
+- node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE);
+-
+- /* The command does not exist. Do the default */
+- if (!node) {
+-
+- if (cfg_user_svc_default_is_permit(user)) {
+-
+- if (debug & DEBUG_AUTHOR_FLAG)
+- report(LOG_DEBUG, "cmd %s does not exist, permitted by default",
+- cmd);
+-
+- data->status = AUTHOR_STATUS_PASS_ADD;
+- data->num_out_args = 0;
+- if (args)
+- free(args);
+- return(0);
+- }
+-
+- if (debug & DEBUG_AUTHOR_FLAG)
+- report(LOG_DEBUG, "cmd %s does not exist, denied by default",
+- cmd);
+-
+- data->status = AUTHOR_STATUS_FAIL;
+- data->num_out_args = 0;
+- if (args)
+- free(args);
+- return(0);
+- }
+-
+- /* The command exists. The default if nothing matches is DENY */
+- data->status = AUTHOR_STATUS_FAIL;
+- data->num_out_args = 0;
+-
+- for (node=node->value1; node && args; node = node->next) {
+- match = regexec((regexp *) node->value1, args);
+-
+- if (debug & DEBUG_AUTHOR_FLAG) {
+- report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
+- node->line, cmd,
+- node->type == N_permit ? "permit" : "deny",
+- node->value, args, (match ? "" : "no "));
+- }
+-
+- if (!match)
+- continue;
++ args = assemble_args(data);
++ switch (cfg_authorize_cmd(user, cmd, args)) {
+
+- switch (node->type) {
+- case N_permit:
+- if (debug & DEBUG_AUTHOR_FLAG) {
+- report(LOG_DEBUG, "%s %s permitted by line %d",
+- cmd, args, node->line);
+- }
++ case ER_TRUE:
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ break;
+- case N_deny:
+- if (debug & DEBUG_AUTHOR_FLAG) {
+- report(LOG_DEBUG, "%s %s denied by line %d",
+- cmd, args, node->line);
+- }
++
++ case ER_FALSE:
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ break;
+- default:
++
++ case ER_UNKNOWN:
+ data->status = AUTHOR_STATUS_ERROR;
+- data->admin_msg = tac_strdup("Server error illegal configuration node");
+- report(LOG_ERR, "%s: %s %s %s",
+- session.peer, cmd, args, data->admin_msg);
++ data->admin_msg = tac_strdup("Server error illegal configuration");
+ break;
+ }
++
+ if (args)
+ free(args);
+- args = NULL;
+- return (0);
+- }
+- if (args)
+- free(args);
+- return (0);
++ return(0);
+ }
+
++static int is_separator TAC_ARGS((int ch));
++
+ static int
+ is_separator(ch)
+-char ch;
++int ch; /* promoted "char" type */
+ {
+ return (ch == '=' || ch == '*');
+ }
+
++static int arg_ok TAC_ARGS((char *arg));
++
+ /* check an attr=value pair for well-formedness */
+ static int
+ arg_ok(arg)
+@@ -630,6 +596,8 @@ char *arg;
+ }
+
+
++static int match_attrs TAC_ARGS((char *nas_arg, char *server_arg));
++
+ /* return 1 if attrs match, 0 otherwise */
+ static int
+ match_attrs(nas_arg, server_arg)
+@@ -647,6 +615,8 @@ char *nas_arg, *server_arg;
+ return (0);
+ }
+
++static int match_values TAC_ARGS((char *nas_arg, char *server_arg));
++
+ /* return 1 if values match, 0 otherwise */
+ static int
+ match_values(nas_arg, server_arg)
+@@ -671,6 +641,8 @@ char *nas_arg, *server_arg;
+ return(STREQ(nas_arg, server_arg));
+ }
+
++static int mandatory TAC_ARGS((char *arg));
++
+ /* Return 1 if arg is mandatory, 0 otherwise */
+ static int
+ mandatory(arg)
+@@ -690,6 +662,8 @@ char *arg;
+ return (*p == '=');
+ }
+
++static int optional TAC_ARGS((char *arg));
++
+ static int
+ optional(arg)
+ char *arg;
+@@ -699,21 +673,16 @@ char *arg;
+
+ /* PPP-LCP requests are a special case. If they are not explicitly
+ configured, but there are other ppp services explicitly configured,
+- we admit (return 0) any PPP-LCP request */
++ we admit (return 1) any PPP-LCP request */
++
++static int ppp_lcp_allowed TAC_ARGS((const char *user));
+
+ static int
+-ppp_lcp_allowed(svc, protocol, user)
+- int svc;
+- char *user, *protocol;
++ppp_lcp_allowed(user)
++const char *user;
+ {
+- /* This is not a ppp/lcp request. Just Say No */
+- if (!(svc == N_svc_ppp &&
+- protocol &&
+- STREQ(protocol, "lcp")))
+- return(0);
+-
+- /* It is an LCP request. Are there PPP services configured */
+- if (cfg_ppp_is_configured(user, TAC_PLUS_RECURSE)) {
++ /* It is an LCP request. Are there PPP services configured? */
++ if (cfg_get_svc_node(user, N_svc_ppp, NULL /* protocol */, NULL /* svcname */, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG,
+ "ppp/lcp request permitted (ppp is configured for %s)",
+@@ -730,14 +699,16 @@ ppp_lcp_allowed(svc, protocol, user)
+ return(0);
+ }
+
++static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
++
+ /* Return 0 means data->status is valid */
+ static int
+ authorize_svc(user, svc, protocol, svcname, data)
+- char *user;
+- int svc;
+- char *svcname;
+- struct author_data *data;
+- char *protocol; /* valid only if svc == ppp */
++const char *user;
++int svc;
++const char *svcname;
++struct author_data *data;
++const char *protocol; /* valid only if svc == ppp */
+ {
+ int max_args;
+ char **out_args, **outp;
+@@ -747,20 +718,23 @@ authorize_svc(user, svc, protocol, svcname, data)
+ char **cfg_argp;
+ int deny_by_default;
+ NODE *node;
++ int service_permit;
+
+ int replaced = 0;
+ int added = 0;
+ int cfg_cnt;
+
+ /* Does this service exist? */
+- node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE);
++ service_permit = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE, &node);
+
+- if (!node) {
+- /* Service not found. If the default is permit, or this is an
+- PPP/LCP request and other ppp services are configured,
+- we'll allow it. */
++ /* Now we may have three cases:
++ * service_permit == 1 && node != NULL
++ * service_permit == 1 && node == NULL
++ * service_permit == 0 (BTW node == NULL)
++ */
+
+- if (cfg_user_svc_default_is_permit(user)) {
++ if (service_permit && !node) {
++ /* Service not found and the default is permit, we'll allow it. */
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+@@ -775,7 +749,13 @@ authorize_svc(user, svc, protocol, svcname, data)
+ return(0);
+ }
+
+- if (ppp_lcp_allowed(svc, protocol, user)) {
++ if (!service_permit) {
++ /* Service not found, if this is an
++ PPP/LCP request and other ppp services are configured,
++ we'll allow it. */
++
++ if (svc == N_svc_ppp && protocol && STREQ(protocol, "lcp")
++ && ppp_lcp_allowed(user)) {
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+@@ -1200,6 +1180,8 @@ authorize_svc(user, svc, protocol, svcname, data)
+ name in svcname.
+ */
+
++static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
++
+ static int
+ get_nas_svc(data, cmdname, protocol, svcname)
+ struct author_data *data;
+diff --git a/do_author.h b/do_author.h
+new file mode 100644
+index 0000000..08bea16
+--- /dev/null
++++ b/do_author.h
+@@ -0,0 +1,64 @@
++#ifndef DO_AUTHOR_H
++#define DO_AUTHOR_H 1
++
++#include "tac_plus.h"
++
++
++/*
++ * This structure describes a principal that is to be authenticated.
++ * username is the principals name (ASCII, null terminated)
++ * NAS_name is the name of the NAS where the user is
++ * NAS_port is the port on the NAS where the user is
++ * NAC_address is the remote user location. This may be
++ * a remote IP address or a caller-ID or ...
++ * priv_lvl user's requested privilege level.
++ */
++
++struct identity {
++ char *username;
++ char *NAS_name;
++ char *NAS_port;
++ char *NAC_address;
++ int priv_lvl;
++};
++
++
++/*
++ * This structure is the data structure for passing information to
++ * and from the authorization function (do_author()).
++ */
++struct author_data {
++ struct identity *id; /* user id */
++ int authen_method; /* authentication method */
++
++#define AUTHEN_METH_NONE 0x01
++#define AUTHEN_METH_KRB5 0x02
++#define AUTHEN_METH_LINE 0x03
++#define AUTHEN_METH_ENABLE 0x04
++#define AUTHEN_METH_LOCAL 0x05
++#define AUTHEN_METH_TACACSPLUS 0x06
++#define AUTHEN_METH_RCMD 0x20
++
++ int authen_type; /* authentication type see authen_type */
++ int service; /* calling service */
++ char *msg; /* optional NULL-terminated return message */
++ char *admin_msg; /* optional NULL-terminated admin message */
++ int status; /* return status */
++
++#define AUTHOR_STATUS_PASS_ADD 0x01
++#define AUTHOR_STATUS_PASS_REPL 0x02
++#define AUTHOR_STATUS_FAIL 0x10
++#define AUTHOR_STATUS_ERROR 0x11
++
++ int num_in_args; /* input arg count */
++ char **input_args; /* input arguments */
++ int num_out_args; /* output arg cnt */
++ char **output_args; /* output arguments */
++
++};
++
++
++extern int do_author TAC_ARGS((struct author_data *data));
++
++
++#endif /* DO_AUTHOR_H */
+diff --git a/dump.c b/dump.c
+index ae58e5e..511085b 100644
+--- a/dump.c
++++ b/dump.c
+@@ -17,8 +17,21 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include /* for ntohl() */
++
++#include "dump.h"
++#include "report.h"
++#include "utils.h"
++#include "packet.h"
++#include "do_author.h"
++#include "main.h"
++
++
++char *summarise_outgoing_packet_type TAC_ARGS((u_char *pak));
++
+ /* Routines for dumping packets to stderr */
+ char *
+ summarise_outgoing_packet_type(pak)
+@@ -90,7 +103,9 @@ u_char *pak;
+ return (p);
+ }
+
+-void
++static void dump_header TAC_ARGS((u_char *pak));
++
++static void
+ dump_header(pak)
+ u_char *pak;
+ {
+@@ -117,7 +132,10 @@ u_char *pak;
+ }
+
+
++void dump_nas_pak TAC_ARGS((u_char *pak));
++
+ /* Dump packets originated by a NAS */
++void
+ dump_nas_pak(pak)
+ u_char *pak;
+ {
+@@ -376,6 +394,9 @@ u_char *pak;
+
+ /* Dump packets originated by Tacacsd */
+
++void dump_tacacs_pak TAC_ARGS((u_char *pak));
++
++void
+ dump_tacacs_pak(pak)
+ u_char *pak;
+ {
+@@ -482,6 +503,8 @@ u_char *pak;
+ report(LOG_DEBUG, "End packet");
+ }
+
++char *summarise_incoming_packet_type TAC_ARGS((u_char *pak));
++
+ /* summarise packet types for logging routines. */
+ char *
+ summarise_incoming_packet_type(pak)
+diff --git a/dump.h b/dump.h
+new file mode 100644
+index 0000000..bddce30
+--- /dev/null
++++ b/dump.h
+@@ -0,0 +1,15 @@
++#ifndef DUMP_H
++#define DUMP_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++extern char *summarise_outgoing_packet_type TAC_ARGS((u_char *pak));
++extern void dump_nas_pak TAC_ARGS((u_char *pak));
++extern void dump_tacacs_pak TAC_ARGS((u_char *pak));
++extern char *summarise_incoming_packet_type TAC_ARGS((u_char *pak));
++
++
++#endif /* DUMP_H */
+diff --git a/enable.c b/enable.c
+index 63a17eb..3e56c92 100644
+--- a/enable.c
++++ b/enable.c
+@@ -17,8 +17,23 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include
++#include
++
++#include "enable.h"
+ #include "expire.h"
++#include "utils.h"
++#include "report.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
++#include "cfgfile.h"
++
+
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+@@ -30,6 +45,8 @@ struct private_data {
+ int state;
+ };
+
++static void enable TAC_ARGS((char *passwd, struct authen_data *data));
++
+ static void
+ enable(passwd, data)
+ char *passwd;
+@@ -84,6 +101,8 @@ struct authen_data *data;
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int enable_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ enable_fn(data)
+ struct authen_data *data;
+diff --git a/enable.h b/enable.h
+new file mode 100644
+index 0000000..10a4e8a
+--- /dev/null
++++ b/enable.h
+@@ -0,0 +1,12 @@
++#ifndef ENABLE_H
++#define ENABLE_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int enable_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* ENABLE_H */
+diff --git a/encrypt.c b/encrypt.c
+index 60ba5e9..2840916 100644
+--- a/encrypt.c
++++ b/encrypt.c
+@@ -17,8 +17,19 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include
++#include /* for ntohl() */
++
++#include "encrypt.h"
+ #include "md5.h"
++#include "utils.h"
++#include "report.h"
++#include "packet.h"
++#include "main.h"
++
+
+ /*
+ * create_md5_hash(): create an md5 hash of the "session_id", "the user's
+@@ -36,21 +47,25 @@
+ *
+ */
+
+-void
++static void create_md5_hash TAC_ARGS((int session_id, const char *key, unsigned version, unsigned seq_no, u_char *prev_hash, u_char *hash));
++
++static void
+ create_md5_hash(session_id, key, version, seq_no, prev_hash, hash)
+ int session_id;
+-char *key;
+-u_char version;
+-u_char seq_no;
++const char *key;
++unsigned version; /* promoted "u_char" type */
++unsigned seq_no; /* promoted "u_char" type */
+ u_char *prev_hash;
+ u_char *hash;
+ {
+ u_char *md_stream, *mdp;
+ int md_len;
+ MD5_CTX mdcontext;
++ u_char version_uchar = version;
++ u_char seq_no_uchar = seq_no;
+
+- md_len = sizeof(session_id) + strlen(key) + sizeof(version) +
+- sizeof(seq_no);
++ md_len = sizeof(session_id) + strlen(key) + sizeof(version_uchar) +
++ sizeof(seq_no_uchar);
+
+ if (prev_hash) {
+ md_len += MD5_LEN;
+@@ -62,11 +77,11 @@ u_char *hash;
+ bcopy(key, mdp, strlen(key));
+ mdp += strlen(key);
+
+- bcopy(&version, mdp, sizeof(version));
+- mdp += sizeof(version);
++ bcopy(&version_uchar, mdp, sizeof(version_uchar));
++ mdp += sizeof(version_uchar);
+
+- bcopy(&seq_no, mdp, sizeof(seq_no));
+- mdp += sizeof(seq_no);
++ bcopy(&seq_no_uchar, mdp, sizeof(seq_no_uchar));
++ mdp += sizeof(seq_no_uchar);
+
+ if (prev_hash) {
+ bcopy(prev_hash, mdp, MD5_LEN);
+@@ -90,10 +105,13 @@ u_char *hash;
+ * Return 0 on success, -1 on failure.
+ */
+
++int md5_xor TAC_ARGS((HDR *hdr, u_char *data, const char *key));
++
++int
+ md5_xor(hdr, data, key)
+ HDR *hdr;
+ u_char *data;
+-char *key;
++const char *key;
+ {
+ int i, j;
+ u_char hash[MD5_LEN]; /* the md5 hash */
+diff --git a/encrypt.h b/encrypt.h
+new file mode 100644
+index 0000000..9c7ae53
+--- /dev/null
++++ b/encrypt.h
+@@ -0,0 +1,14 @@
++#ifndef ENCRYPT_H
++#define ENCRYPT_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++#include "packet.h" /* for "HDR" */
++
++
++extern int md5_xor TAC_ARGS((HDR *hdr, u_char *data, const char *key));
++
++
++#endif /* ENCRYPT_H */
+diff --git a/expire.c b/expire.c
+index 1ef745c..3aed354 100644
+--- a/expire.c
++++ b/expire.c
+@@ -17,9 +17,17 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include
++#include
++#include
++#include
++
+ #include "expire.h"
+
++
+ /*
+ * check a date for expiry. If the field specifies
+ * a shell return PW_OK
+@@ -37,9 +45,11 @@ static char *monthname[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+ static long days_ere_month[] = {0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334};
+
++int check_expiration TAC_ARGS((const char *date));
++
+ int
+ check_expiration(date)
+-char *date;
++const char *date;
+ {
+ long day, month, year, leaps, now, expiration, warning;
+ char monthstr[10];
+@@ -52,11 +62,11 @@ char *date;
+ return (PW_OK);
+
+ /* Parse date string. Fail it upon error. */
+- if (sscanf(date, "%s %d %d", monthstr, &day, &year) != 3)
++ if (sscanf(date, "%s %ld %ld", monthstr, &day, &year) != 3)
+ return (PW_EXPIRED);
+
+ for(i=0; i < 3; i++) {
+- monthstr[i] = toupper(monthstr[i]);
++ monthstr[i] = toupper((int) monthstr[i]);
+ }
+
+ /* Compute the expiration date in days. */
+diff --git a/expire.h b/expire.h
+index c7df48d..6e7fc5e 100644
+--- a/expire.h
++++ b/expire.h
+@@ -1,3 +1,8 @@
++#ifndef EXPIRE_H
++#define EXPIRE_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,10 +22,15 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #define PW_OK 0 /* pw not expired and not due to expire soon */
+ #define PW_EXPIRED 1 /* pw has expired */
+ #define PW_EXPIRING 2 /* pw will expire soon */
+
+ #define MAX_PASSWD_LEN 256
+
+-extern int check_expiration();
++
++extern int check_expiration TAC_ARGS((const char *date));
++
++
++#endif /* EXPIRE_H */
+diff --git a/generate_passwd.c b/generate_passwd.c
+index 509e48a..a21098d 100644
+--- a/generate_passwd.c
++++ b/generate_passwd.c
+@@ -23,12 +23,41 @@
+ Usage: a.out [salt]
+ */
+
+-#define NULL 0
+
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++
++#ifdef HAVE_UNISTD_H
++#include
++#endif
++#include
++#include
++#include
++#include
++
++
++#ifndef NULL
++#define NULL ((void *) 0)
++#endif
++
++/* Stolen from pbmplus.h of netpbm: */
++
++#if __STDC__
++#define TAC_ARGS(alist) alist
++#else /*__STDC__*/
++#define TAC_ARGS(alist) ()
++#endif /*__STDC__*/
++
++
++int main TAC_ARGS((int argc, char **argv));
++
++int
+ main(argc, argv)
++int argc;
+ char **argv;
+ {
+- char *crypt();
+ char pass[25], *salt, buf[24];
+ char *result;
+ int n;
+@@ -42,10 +71,10 @@ char **argv;
+
+ write(1, prompt, strlen(prompt));
+ n = read(0, pass, sizeof(pass));
+- pass[n-1] = NULL;
++ pass[n-1] = 0;
+
+ if (!salt) {
+- int i, r, r1, r2;
++ int i, r, r1 = 0 /* GCC paranoia */, r2 = 0 /* GCC paranoia */;
+
+ srand(time(0));
+
+@@ -82,9 +111,6 @@ char **argv;
+
+ write(1, result, strlen(result));
+ write(1, "\n", 1);
+-}
+-
+-
+-
+-
+
++ return (EXIT_SUCCESS);
++}
+diff --git a/hash.c b/hash.c
+index 443d42b..2db807b 100644
+--- a/hash.c
++++ b/hash.c
+@@ -17,19 +17,24 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include "hash.h"
++#include "utils.h"
++
++
+ struct entry {
+ char *name;
+ void *hash;
+ };
+
+-typedef struct entry ENTRY;
++static int calculate_hash TAC_ARGS((const char *name));
+
+ /* Calculate hash value from a string */
+ static int
+ calculate_hash(name)
+-char *name;
++const char *name;
+ {
+ int i;
+ int len = strlen(name);
+@@ -43,12 +48,14 @@ char *name;
+ return (hashval);
+ }
+
++void *hash_lookup TAC_ARGS((void **hashtab, const char *name));
++
+ /* Lookup a name in a hash table. Return its node if it exists, NULL
+ otherwise */
+ void *
+ hash_lookup(hashtab, name)
+ void **hashtab;
+-char *name;
++const char *name;
+ {
+ ENTRY *entry;
+ int hashval = calculate_hash(name);
+@@ -64,6 +71,8 @@ char *name;
+ return (NULL);
+ }
+
++void *hash_add_entry TAC_ARGS((void **hashtab, ENTRY *newentry));
++
+ /* Add a node to a hash table. Return node if it exists, NULL
+ otherwise */
+ void *
+@@ -86,6 +95,8 @@ ENTRY *newentry;
+ }
+
+
++void **hash_get_entries TAC_ARGS((void **hashtab));
++
+ /* Return an array of pointers to all the entries in a hash table */
+ void **
+ hash_get_entries(hashtab)
+diff --git a/hash.h b/hash.h
+new file mode 100644
+index 0000000..a808f05
+--- /dev/null
++++ b/hash.h
+@@ -0,0 +1,17 @@
++#ifndef HASH_H
++#define HASH_H 1
++
++#include "tac_plus.h"
++
++
++#define HASH_TAB_SIZE 157 /* user and group hash table sizes */
++
++typedef struct entry ENTRY;
++
++
++extern void *hash_lookup TAC_ARGS((void **hashtab, const char *name));
++extern void *hash_add_entry TAC_ARGS((void **hashtab, ENTRY *newentry));
++extern void **hash_get_entries TAC_ARGS((void **hashtab));
++
++
++#endif /* HASH_H */
+diff --git a/ldap_author.c b/ldap_author.c
+index ca1a8ef..c788e82 100644
+--- a/ldap_author.c
++++ b/ldap_author.c
+@@ -36,21 +36,30 @@ Port name isn't required.. I would like to change format with :
+ */
+
+
+-#if defined(USE_LDAP)
++#include "tac_plus.h"
++
++#ifdef USE_LDAP
++
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+
+-#include "tac_plus.h"
+-#include "ldap.h"
++#include "ldap_author.h"
++#include "main.h"
++#include "utils.h"
++#include "report.h"
++
+
++int ldap_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
+
+ int
+ ldap_verify(user, users_passwd, str_conn)
+-char *user, *users_passwd; /* Username and gived password */
+-char *str_conn; /* String connection to database */
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *str_conn; /* string connection to database */
+ {
+ char *buf;
+ char *ldapServer;
+@@ -62,11 +71,7 @@ char *str_conn; /* String connection to database */
+ /* Don't allow null username and passwd */
+ if ( *user == '0' || *users_passwd == '0' ) return (1);
+
+- buf=(char *)malloc(strlen(str_conn)+1);
+- if (buf == NULL ){
+- report(LOG_DEBUG, "Error can't allocate memory");
+- return(1);
+- }
++ buf = (char *) tac_malloc(strlen(str_conn)+1);
+
+ strcpy(buf,str_conn);
+ ldapServer=strstr(buf, "://");
+@@ -99,7 +104,7 @@ char *str_conn; /* String connection to database */
+ return 1;
+ }
+
+- err=ldap_simple_bind_s(ld, user, users_passwd);
++ err=ldap_simple_bind_s(ld, (/* de-const */ char *) user, (/* de-const */ char *) users_passwd);
+
+ if(err != LDAP_SUCCESS)
+ {
+@@ -116,4 +121,9 @@ char *str_conn; /* String connection to database */
+ return 0;
+ }
+ }
+-#endif /* LDAP */
++
++#else /* USE_LDAP */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* USE_LDAP */
+diff --git a/ldap_author.h b/ldap_author.h
+index 6479426..ecd71dd 100644
+--- a/ldap_author.h
++++ b/ldap_author.h
+@@ -1 +1,14 @@
+-int ldap_verify();
++#ifndef LDAP_AUTHOR_H
++#define LDAP_AUTHOR_H 1
++
++#include "tac_plus.h"
++
++#ifdef USE_LDAP
++
++
++extern int ldap_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
++
++
++#endif /* USE_LDAP */
++
++#endif /* LDAP_AUTHOR_H */
+diff --git a/main.c b/main.c
+index cf0ffd8..c356b75 100644
+--- a/main.c
++++ b/main.c
+@@ -14,7 +14,7 @@
+ * distribution of the program without specific prior permission, and
+ * notice be given in supporting documentation that modification,
+ * copying and distribution is by permission of Cisco Systems, Inc.
+-
++ *
+ * Cisco Systems, Inc. makes no representations about the suitability
+ * of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
+ * IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+@@ -22,37 +22,87 @@
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+-#include "sys/wait.h"
+-#include "signal.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#ifdef HAVE_SYS_TIME_H
++#include
++#endif
++#ifdef HAVE_UNISTD_H
++#include
++#endif
++#ifdef HAVE_SYSLOG_H
++#include
++#endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include
++#endif
++#ifdef HAVE_FCNTL_H
++#include
++#endif
++#ifdef HAVE_SYS_IOCTL_H
++#include
++#endif
++
++#include "main.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "packet.h"
++#include "dump.h"
++#include "author.h"
++#include "acct.h"
++#include "authen.h"
++#include "do_acct.h"
++#include "parse.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++
++
++static void version TAC_ARGS((void));
++static void start_session TAC_ARGS((void));
++
++
++/* Configurable:
++ */
++
++#ifndef TAC_PLUS_PORT
++#define TAC_PLUS_PORT 49
++#endif
++
+
+ static int standalone = 1; /* running standalone (1) or under inetd (0) */
+ static int initialised = 0; /* data structures have been allocated */
+ int sendauth_only = 0; /* don't respond to sendpass requests */
+ int debug = 0; /* debugging flags */
+-int port = 0; /* port we're listening on */
++static int port = 0; /* port we're listening on */
+ int console = 0; /* write all syslog messages to console */
+ int parse_only = 0; /* exit after verbose parsing */
+ int single = 0; /* single thread (for debugging) */
+-int wtmpfd = 0; /* for wtmp file logging */
+-char *wtmpfile = NULL;
+-
+-struct timeval started_at;
+
+ struct session session; /* session data */
+
+ static char pidfilebuf[75]; /* holds current name of the pidfile */
+
+-void start_session();
+
+ #ifndef REAPCHILD
+-static
+-#ifdef VOIDSIG
+-void
+-#else
+-int
+-#endif /* VOIDSIG */
+-reapchild()
++static RETSIGTYPE reapchild TAC_ARGS((int signo));
++
++static RETSIGTYPE
++reapchild(signo)
++int signo;
+ {
+ #ifdef UNIONWAIT
+ union wait status;
+@@ -61,6 +111,8 @@ reapchild()
+ #endif
+ int pid;
+
++ signal(SIGCHLD, reapchild);
++
+ for (;;) {
+ pid = wait3(&status, WNOHANG, 0);
+ if (pid <= 0)
+@@ -71,6 +123,8 @@ reapchild()
+ }
+ #endif /* REAPCHILD */
+
++static void die TAC_ARGS((int signum));
++
+ static void
+ die(signum)
+ int signum;
+@@ -80,6 +134,8 @@ int signum;
+ tac_exit(0);
+ }
+
++static void init TAC_ARGS((void));
++
+ static void
+ init()
+ {
+@@ -104,26 +160,59 @@ init()
+
+ initialised++;
+
+- report(LOG_INFO, "Version %s Initialized %d", VERSION, initialised);
++ report(LOG_INFO, "Version %s%s Initialized %d", VERSION, VERSION_TAIL, initialised);
+
+ }
+
+-static void
++/* 'handler()' will be called during initialization to setup signal handler,
++ * keep it in mind when modifying it!
++ */
++
++static int handler_occured = 0;
++
++static RETSIGTYPE handler TAC_ARGS((int signum));
++
++static RETSIGTYPE
+ handler(signum)
+ int signum;
+ {
+- report(LOG_INFO, "Received signal %d", signum);
+- init();
+-#ifdef REARMSIGNAL
++ /* never execute any non-trivial (=system-call) commands here
++ * as it may immediately abort the whole 'handler()' (SYSV signal).
++ * We hope that SA_RESTART is NOT set for our signals
++ */
++ handler_occured = 1;
++ /* It is never wrong to reinstall 'handler' just to be safe */
+ signal(SIGUSR1, handler);
+- signal(SIGHUP, handler);
+-#endif REARMSIGNAL
++ signal(SIGHUP , handler);
++
++ /* DON'T interrupt! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 0 /* flag */);
++ siginterrupt(SIGHUP , 0 /* flag */);
++#endif
++}
++
++static void check_handler_occured TAC_ARGS((void));
++
++static RETSIGTYPE
++check_handler_occured()
++{
++
++ if (!handler_occured)
++ return;
++
++ handler_occured = 0;
++ report(LOG_INFO, "Signal detected, reloading configuration");
++ init();
+ }
+
+ /*
+ * Return a socket bound to an appropriate port number/address. Exits
+ * the program on failure */
+
++static int get_socket TAC_ARGS((void));
++
++static int
+ get_socket()
+ {
+ int s;
+@@ -171,6 +260,8 @@ get_socket()
+ return (s);
+ }
+
++static void open_logfile TAC_ARGS((void));
++
+ static void
+ open_logfile()
+ {
+@@ -182,6 +273,29 @@ open_logfile()
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ }
+
++static void prep_session_peer TAC_ARGS((const struct sockaddr_in *from));
++
++static void
++prep_session_peer(from)
++const struct sockaddr_in *from;
++{
++ struct hostent *hp;
++
++ if (session.peer_addr && session.peer_addr != session.peer)
++ free(session.peer_addr);
++ if (session.peer)
++ free(session.peer);
++
++ session.peer_addr = tac_strdup( (char *) inet_ntoa(from->sin_addr) );
++
++ hp = gethostbyaddr((char *) &from->sin_addr.s_addr, sizeof(from->sin_addr.s_addr), AF_INET);
++
++ if (hp)
++ session.peer = tac_strdup(hp->h_name);
++ else
++ session.peer = session.peer_addr;
++}
++
+ /*
+ * main
+ *
+@@ -189,6 +303,9 @@ open_logfile()
+ * Parse arguments and act appropiately.
+ */
+
++int main TAC_ARGS((int argc, char **argv));
++
++int
+ main(argc, argv)
+ int argc;
+ char **argv;
+@@ -214,6 +331,10 @@ char **argv;
+ port = TAC_PLUS_PORT;
+ #endif
+
++#ifdef MAINTAINER_MODE
++ session.cfgfile = "/etc/tacacs/tac_plus.cfg";
++#endif
++
+ if (argc <= 1) {
+ fprintf(stderr, "Usage: tac_plus -C \n");
+ fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p ]\n");
+@@ -284,8 +405,9 @@ char **argv;
+
+ init();
+
+- signal(SIGUSR1, handler);
+- signal(SIGHUP, handler);
++ handler(-1 /* signum */); /* connect to the signals */
++ handler_occured = 0; /* post-fix cludge */
++
+ signal(SIGTERM, die);
+ signal(SIGPIPE, SIG_IGN);
+
+@@ -293,31 +415,27 @@ char **argv;
+ tac_exit(0);
+
+ if (debug)
+- report(LOG_DEBUG, "tac_plus server %s starting", VERSION);
++ report(LOG_DEBUG, "tac_plus server %s%s starting", VERSION, VERSION_TAIL);
+
+ if (!standalone) {
+ /* running under inetd */
+ struct sockaddr_in name;
+- int name_len;
+- int on = 1;
++ socklen_t name_len;
++#ifdef FIONBIO
++ int fionbio_on = 1;
++#endif
+
+ name_len = sizeof(name);
+
+- session.sock = 0;
+ if (getpeername(session.sock, (struct sockaddr *) &name, &name_len)) {
+ report(LOG_ERR, "getpeername failure %s", sys_errlist[errno]);
+- } else {
+- struct hostent *hp;
+- hp = gethostbyaddr((char *) &name.sin_addr.s_addr,
+- sizeof(name.sin_addr.s_addr), AF_INET);
+- if (session.peer) {
+- free(session.peer);
+- }
+- session.peer = tac_strdup(hp ? hp->h_name :
+- (char *) inet_ntoa(name.sin_addr));
+- }
++ prep_session_peer(NULL);
++ } else
++ prep_session_peer(&name);
++
++ session.sock = 0;
+ #ifdef FIONBIO
+- if (ioctl(session.sock, FIONBIO, &on) < 0) {
++ if (ioctl(session.sock, FIONBIO, &fionbio_on) < 0) {
+ report(LOG_ERR, "ioctl(FIONBIO) %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+@@ -351,23 +469,29 @@ char **argv;
+
+ #ifndef REAPCHILD
+
+-#ifdef LINUX
++#ifdef SETPGRP_VOID
+ if (setpgrp() == -1)
+-#else /* LINUX */
++#else /* SETPGRP_VOID */
+ if (setpgrp(0, getpid()) == -1)
+-#endif /* LINUX */
++#endif /* SETPGRP_VOID */
+ report(LOG_ERR, "Can't change process group");
+
++#ifdef TIOCNOTTY
+ c = open("/dev/tty", O_RDWR);
+ if (c >= 0) {
+ ioctl(c, TIOCNOTTY, (char *) 0);
+ (void) close(c);
+ }
++#endif
+ signal(SIGCHLD, reapchild);
+
+ #else /* REAPCHILD */
+
++#ifdef SETPGRP_VOID
+ if (setpgrp() == 1)
++#else /* SETPGRP_VOID */
++ if (setpgrp(0, getpid()) == 1)
++#endif /* SETPGRP_VOID */
+ report(LOG_ERR, "Can't change process group");
+
+ signal(SIGHUP, SIG_IGN);
+@@ -423,7 +547,7 @@ char **argv;
+
+ /* write process id to pidfile */
+ if ((fp = fopen(pidfilebuf, "w")) != NULL) {
+- fprintf(fp, "%d\n", getpid());
++ fprintf(fp, "%d\n", (int) getpid());
+ fclose(fp);
+ } else
+ report(LOG_ERR, "Cannot write pid to %s %s",
+@@ -446,38 +570,45 @@ char **argv;
+ #endif /* MAXSESS */
+
+ report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
+- getuid(), geteuid(), getgid(), getegid(), s);
++ (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid(), s);
+
+ for (;;) {
+ int pid;
+ struct sockaddr_in from;
+- int from_len;
++ socklen_t from_len;
+ int newsockfd;
+- struct hostent *hp = NULL;
++
++ check_handler_occured();
+
+ bzero((char *) &from, sizeof(from));
+ from_len = sizeof(from);
+
++ /* PERMIT interrupt of accept()! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 1 /* flag */);
++ siginterrupt(SIGHUP , 1 /* flag */);
++#endif
++
++ errno = 0;
+ newsockfd = accept(s, (struct sockaddr *) &from, &from_len);
+
++ /* DON'T interrupt! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 0 /* flag */);
++ siginterrupt(SIGHUP , 0 /* flag */);
++#endif
++
++ check_handler_occured();
++
+ if (newsockfd < 0) {
+- if (errno == EINTR)
++ /* sometimes we may get even 'errno==0' when 'handler()' signal occured */
++ if (errno == EINTR || errno == 0)
+ continue;
+
+ report(LOG_ERR, "accept: %s", sys_errlist[errno]);
+ continue;
+ }
+-
+- if (lookup_peer) {
+- hp = gethostbyaddr((char *) &from.sin_addr.s_addr,
+- sizeof(from.sin_addr.s_addr), AF_INET);
+- }
+-
+- if (session.peer) {
+- free(session.peer);
+- }
+- session.peer = tac_strdup(hp ? hp->h_name :
+- (char *) inet_ntoa(from.sin_addr));
++ prep_session_peer(&from);
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "session request from %s sock=%d",
+@@ -521,8 +652,10 @@ getdtablesize()
+ }
+ #endif /* GETDTABLESIZE */
+
++static int bad_version_check TAC_ARGS((u_char *pak));
++
+ /* Make sure version number is kosher. Return 0 if it is */
+-int
++static int
+ bad_version_check(pak)
+ u_char *pak;
+ {
+@@ -555,17 +688,21 @@ u_char *pak;
+ *
+ */
+
+-void
++static void start_session TAC_ARGS((void));
++
++static void
+ start_session()
+ {
+- u_char *pak, *read_packet();
++ u_char *pak;
+ HDR *hdr;
+- void authen();
+
+ session.seq_no = 0;
+ session.aborted = 0;
+ session.version = 0;
+
++ /* Now we are starting our new 'request' cycle */
++ cfg_request_scan_begin();
++
+ pak = read_packet();
+ if (!pak) {
+ return;
+@@ -608,9 +745,12 @@ start_session()
+ }
+ }
+
++static void version TAC_ARGS((void));
++
++static void
+ version()
+ {
+- fprintf(stdout, "tac_plus version %s\n", VERSION);
++ fprintf(stdout, "tac_plus version %s%s\n", VERSION, VERSION_TAIL);
+ #ifdef AIX
+ fprintf(stdout,"AIX\n");
+ #endif
+@@ -644,9 +784,6 @@ version()
+ #ifdef LINUX
+ fprintf(stdout,"LINUX\n");
+ #endif
+-#ifdef LITTLE_ENDIAN
+- fprintf(stdout,"LITTLE_ENDIAN\n");
+-#endif
+ #ifdef LOG_LOCAL6
+ fprintf(stdout,"LOG_LOCAL6\n");
+ #endif
+@@ -662,15 +799,9 @@ version()
+ #ifdef NETBSD
+ fprintf(stdout,"NETBSD\n");
+ #endif
+-#ifdef NO_PWAGE
+- fprintf(stdout,"NO_PWAGE\n");
+-#endif
+ #ifdef REAPCHILD
+ fprintf(stdout,"REAPCHILD\n");
+ #endif
+-#ifdef REARMSIGNAL
+- fprintf(stdout,"REARMSIGNAL\n");
+-#endif
+ #ifdef SHADOW_PASSWORDS
+ fprintf(stdout,"SHADOW_PASSWORDS\n");
+ #endif
+@@ -692,14 +823,8 @@ version()
+ #ifdef SO_REUSEADDR
+ fprintf(stdout,"SO_REUSEADDR\n");
+ #endif
+-#ifdef STDLIB_MALLOC
+- fprintf(stdout,"STDLIB_MALLOC\n");
+-#endif
+-#ifdef STRCSPN
+- fprintf(stdout,"STRCSPN\n");
+-#endif
+-#ifdef SYSLOG_IN_SYS
+- fprintf(stdout,"SYSLOG_IN_SYS\n");
++#ifdef HAVE_STRCSPN
++ fprintf(stdout,"HAVE_STRCSPN\n");
+ #endif
+ #ifdef SYSV
+ fprintf(stdout,"SYSV\n");
+@@ -713,15 +838,9 @@ version()
+ #ifdef TACPLUS_USERID
+ fprintf(stdout,"TACPLUS_USERID\n");
+ #endif
+-#ifdef TRACE
+- fprintf(stdout,"TRACE\n");
+-#endif
+ #ifdef UNIONWAIT
+ fprintf(stdout,"UNIONWAIT\n");
+ #endif
+-#ifdef VOIDSIG
+- fprintf(stdout,"VOIDSIG\n");
+-#endif
+ #ifdef _BSD1
+ fprintf(stdout,"_BSD1\n");
+ #endif
+diff --git a/main.h b/main.h
+new file mode 100644
+index 0000000..79c643a
+--- /dev/null
++++ b/main.h
+@@ -0,0 +1,59 @@
++#ifndef MAIN_H
++#define MAIN_H 1
++
++#include "tac_plus.h"
++
++#include /* for u_* */
++
++
++#define NAS_PORT_MAX_LEN 255
++
++struct session {
++ int session_id; /* host specific unique session id */
++ int aborted; /* have we received an abort flag? */
++ int seq_no; /* seq. no. of last packet exchanged */
++ time_t last_exch; /* time of last packet exchange */
++ int sock; /* socket for this connection */
++ char *key; /* the key */
++ int keyline; /* line number key was found on */
++ char *peer; /* name of connected peer */
++ char *peer_addr; /* numerical name of connected peer */
++ /* it MAY (peer==peer_addr)! */
++ char *cfgfile; /* config file name */
++ char *acctfile; /* name of accounting file */
++ char *db_acct; /* name of db accounting string */
++ char port[NAS_PORT_MAX_LEN+1]; /* For error reporting */
++ u_char version; /* version of last packet read */
++};
++
++extern int debug; /* debugging flag */
++extern int single; /* do not fork (for debugging) */
++extern int console; /* log to console */
++extern int parse_only; /* exit after parsing verbosely */
++extern int sendauth_only; /* don't do sendauth */
++
++/* Debugging flags */
++
++#define DEBUG_PARSE_FLAG 2
++#define DEBUG_FORK_FLAG 4
++#define DEBUG_AUTHOR_FLAG 8
++#define DEBUG_AUTHEN_FLAG 16
++#define DEBUG_PASSWD_FLAG 32
++#define DEBUG_ACCT_FLAG 64
++#define DEBUG_CONFIG_FLAG 128
++#define DEBUG_PACKET_FLAG 256
++#define DEBUG_HEX_FLAG 512
++#define DEBUG_MD5_HASH_FLAG 1024
++#define DEBUG_XOR_FLAG 2048
++#define DEBUG_CLEAN_FLAG 4096
++#define DEBUG_SUBST_FLAG 8192
++#define DEBUG_CFGEVAL_FLAG 16384
++#define DEBUG_MAXSESS_FLAG 32768
++#define DEBUG_LOCK_FLAG 65536
++
++
++extern struct session session; /* the session */
++extern int main TAC_ARGS((int argc, char **argv));
++
++
++#endif /* MAIN_H */
+diff --git a/maxsess.c b/maxsess.c
+index 83e0815..aa81a9a 100644
+--- a/maxsess.c
++++ b/maxsess.c
+@@ -17,9 +17,62 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
+ #ifdef MAXSESS
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#ifdef HAVE_FCNTL_H
++#include
++#endif
++#include
++#include
++#ifdef HAVE_SYS_TIME_H
++#include
++#endif
++#ifdef HAVE_UNISTD_H
++#include
++#endif
++#include
++
++#include "maxsess.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "main.h"
++#include "do_acct.h"
++#include "parse.h"
++
++
++void maxsess_loginit TAC_ARGS((void));
++
++
++/* Configurable:
++ */
++
++/* This is a shared file used to maintain a record of who's on
++ */
++#define WHOLOG_DEFAULT "/var/log/tac_who.log"
++
++
++/*
++ * This is state kept per user/session
++ */
++struct peruser {
++ char username[64]; /* User name */
++ char NAS_name[32]; /* NAS user logged into */
++ char NAS_port[32]; /* ...port on that NAS */
++ char NAC_address[32]; /* ...IP address of NAS */
++};
++
++
+ char *wholog = WHOLOG_DEFAULT;
+ /*
+ * initialize wholog file for tracking of user logins/logouts from
+@@ -41,6 +94,8 @@ maxsess_loginit()
+ }
+ }
+
++static char *portname TAC_ARGS((char *oldport));
++
+ /*
+ * Given a port description, return it in a canonical format.
+ *
+@@ -67,6 +122,8 @@ char *oldport;
+ return (p);
+ }
+
++static void write_record TAC_ARGS((char *name, FILE *fp, void *buf, int size, long int offset));
++
+ /*
+ * Seek to offset and write a buffer into the file pointed to by fp
+ */
+@@ -79,7 +136,7 @@ void *buf;
+ char *name;
+ {
+ if (fseek(fp, offset, SEEK_SET) < 0) {
+- report(LOG_ERR, "%s fd=%d Cannot seek to %d %s",
++ report(LOG_ERR, "%s fd=%d Cannot seek to %ld %s",
+ name, fileno(fp), offset, sys_errlist[errno]);
+ }
+ if (fwrite(buf, size, 1, fp) != 1) {
+@@ -88,6 +145,8 @@ char *name;
+ }
+ }
+
++static void process_stop_record TAC_ARGS((struct identity *idp));
++
+ static void
+ process_stop_record(idp)
+ struct identity *idp;
+@@ -133,6 +192,8 @@ struct identity *idp;
+ fclose(fp);
+ }
+
++static void process_start_record TAC_ARGS((struct identity *idp));
++
+ static void
+ process_start_record(idp)
+ struct identity *idp;
+@@ -215,10 +276,13 @@ struct identity *idp;
+ }
+
+
++void loguser TAC_ARGS((struct acct_rec *rec));
++
+ /*
+ * Given a start or a stop accounting record, update the file of
+ * records which tracks who's logged on and where.
+ */
++void
+ loguser(rec)
+ struct acct_rec *rec;
+ {
+@@ -260,10 +324,12 @@ struct acct_rec *rec;
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes read. */
+
+-int
++static int timed_read TAC_ARGS((int fd, void *ptr, int nbytes, int timeout));
++
++static int
+ timed_read(fd, ptr, nbytes, timeout)
+ int fd;
+-u_char *ptr;
++void *ptr;
+ int nbytes;
+ int timeout;
+ {
+@@ -348,6 +414,8 @@ int timeout;
+ * with a maximum possible width of 10.
+ */
+
++static int ckfinger TAC_ARGS((char *user, char *nas, struct identity *idp));
++
+ static int
+ ckfinger(user, nas, idp)
+ char *user, *nas;
+@@ -372,7 +440,7 @@ struct identity *idp;
+
+ /* Get IP addr for the NAS */
+ inaddr = inet_addr(nas);
+- if (inaddr != -1) {
++ if (inaddr != (u_long)-1) {
+ /* A dotted decimal address */
+ bcopy(&inaddr, &sin.sin_addr, sizeof(inaddr));
+ sin.sin_family = AF_INET;
+@@ -492,6 +560,8 @@ struct identity *idp;
+ return (count);
+ }
+
++static int countusers_by_finger TAC_ARGS((struct identity *idp));
++
+ /*
+ * Verify how many sessions a user has according to the wholog file.
+ * Use finger to contact each NAS that wholog says has this user
+@@ -565,6 +635,8 @@ struct identity *idp;
+ return (nsess);
+ }
+
++static int countuser TAC_ARGS((struct identity *idp));
++
+ /*
+ * Estimate how many sessions a named user currently owns by looking in
+ * the wholog file.
+@@ -604,6 +676,8 @@ struct identity *idp;
+ return (nsess);
+ }
+
++static int is_async TAC_ARGS((char *portname));
++
+ /*
+ * is_async()
+ * Tell if the named NAS port is an async-like device.
+@@ -622,6 +696,8 @@ char *portname;
+ return (0);
+ }
+
++int maxsess_check_count TAC_ARGS((char *user, struct author_data *data));
++
+ /*
+ * See if this user can have more sessions.
+ */
+@@ -636,7 +712,7 @@ struct author_data *data;
+ /* No max session configured--don't check */
+ id = data->id;
+
+- maxsess = cfg_get_intvalue(user, TAC_IS_USER, S_maxsess, TAC_PLUS_RECURSE);
++ maxsess = cfg_get_intvalue(S_user, user, S_maxsess, TAC_PLUS_RECURSE);
+ if (!maxsess) {
+ if (debug & (DEBUG_MAXSESS_FLAG | DEBUG_AUTHOR_FLAG)) {
+ report(LOG_DEBUG, "%s may run an unlimited number of sessions",
+@@ -680,13 +756,6 @@ struct author_data *data;
+
+ #else /* MAXSESS */
+
+-/*
+- * The following code is not needed or used. It exists solely to
+- * prevent compilers from "helpfully" complaining that this source
+- * file is empty when MAXSESS is not defined. This upsets novices
+- * building the software, and I get complaints
+- */
+-
+-static int dummy = 0;
++TAC_SOURCEFILE_EMPTY
+
+ #endif /* MAXSESS */
+diff --git a/maxsess.h b/maxsess.h
+new file mode 100644
+index 0000000..c96d2b8
+--- /dev/null
++++ b/maxsess.h
+@@ -0,0 +1,25 @@
++#ifndef MAXSESS_H
++#define MAXSESS_H 1
++
++#include "tac_plus.h"
++
++#ifdef MAXSESS
++
++#include "do_author.h"
++
++
++/* This is a shared file used to maintain a record of who's on
++ */
++extern char *wholog;
++
++
++struct acct_rec;
++
++extern void maxsess_loginit TAC_ARGS((void));
++extern void loguser TAC_ARGS((struct acct_rec *rec));
++extern int maxsess_check_count TAC_ARGS((char *user, struct author_data *data));
++
++
++#endif /* MAXSESS */
++
++#endif /* MAXSESS_H */
+diff --git a/md4.c b/md4.c
+index d99c84c..5ac377d 100644
+--- a/md4.c
++++ b/md4.c
+@@ -43,19 +43,27 @@
+ */
+
+
++#include "tac_plus.h"
++
++#ifdef MSCHAP
++
+ #include