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 ++ + #include "md4.h" + /* + #include "master.h" + #include + */ + ++ + typedef unsigned char *POINTER; + typedef unsigned short int UINT2; + typedef unsigned long int UINT4; + +-#define PROTO_LIST(list) () +-#define const ++static void MD4Transform TAC_ARGS((UINT4 state[4], const unsigned char block[64])); ++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len)); ++static void Decode TAC_ARGS((UINT4 *output, const unsigned char *input, unsigned int len)); ++ + + /* Constants for MD4Transform routine. + */ +@@ -72,12 +80,6 @@ typedef unsigned long int UINT4; + #define S33 11 + #define S34 15 + +-static void MD4Transform PROTO_LIST ((UINT4 [4], const unsigned char [64])); +-static void Encode PROTO_LIST +- ((unsigned char *, UINT4 *, unsigned int)); +-static void Decode PROTO_LIST +- ((UINT4 *, const unsigned char *, unsigned int)); +- + static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +@@ -111,6 +113,9 @@ static unsigned char PADDING[64] = { + + /* MD4 initialization. Begins an MD4 operation, writing a new context. + */ ++ ++void MD4Init TAC_ARGS((MD4_CTX *context)); ++ + void MD4Init (context) + MD4_CTX *context; /* context */ + { +@@ -128,6 +133,9 @@ MD4_CTX *context; /* context */ + operation, processing another message block, and updating the + context. + */ ++ ++void MD4Update TAC_ARGS((MD4_CTX *context, const unsigned char *input, unsigned int inputLen)); ++ + void MD4Update (context, input, inputLen) + MD4_CTX *context; /* context */ + const unsigned char *input; /* input block */ +@@ -168,6 +176,9 @@ unsigned int inputLen; /* length of input block */ + /* MD4 finalization. Ends an MD4 message-digest operation, writing the + the message digest and zeroizing the context. + */ ++ ++void MD4Final TAC_ARGS((unsigned char digest[16], MD4_CTX *context)); ++ + void MD4Final (digest, context) + unsigned char digest[16]; /* message digest */ + MD4_CTX *context; /* context */ +@@ -196,6 +207,9 @@ MD4_CTX *context; /* context */ + + /* MD4 basic transformation. Transforms state based on block. + */ ++ ++static void MD4Transform TAC_ARGS((UINT4 state[4], const unsigned char block[64])); ++ + static void MD4Transform (state, block) + UINT4 state[4]; + const unsigned char block[64]; +@@ -271,6 +285,9 @@ const unsigned char block[64]; + /* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ ++ ++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len)); ++ + static void Encode (output, input, len) + unsigned char *output; + UINT4 *input; +@@ -289,8 +306,10 @@ unsigned int len; + /* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +-static void Decode (output, input, len) + ++static void Decode TAC_ARGS((UINT4 *output, const unsigned char *input, unsigned int len)); ++ ++static void Decode (output, input, len) + UINT4 *output; + const unsigned char *input; + unsigned int len; +@@ -301,3 +320,9 @@ unsigned int len; + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); + } ++ ++#else /* MSCHAP */ ++ ++TAC_SOURCEFILE_EMPTY ++ ++#endif /* MSCHAP */ +diff --git a/md4.h b/md4.h +index d821229..38d0a81 100644 +--- a/md4.h ++++ b/md4.h +@@ -1,3 +1,10 @@ ++#ifndef MD4_H ++#define MD4_H 1 ++ ++#include "tac_plus.h" ++ ++#ifdef MSCHAP ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -42,8 +49,6 @@ + documentation and/or software. + */ + +-#ifndef _MD4_H_ +-#define _MD4_H_ + /* MD4 context. */ + typedef struct MD4Context { + unsigned long int state[4]; /* state (ABCD) */ +@@ -51,11 +56,12 @@ typedef struct MD4Context { + unsigned char buffer[64]; /* input buffer */ + } MD4_CTX; + +-void MD4Init(); +-void MD4Update(); +-void MD4Final(); +-char * MD4End(); +-char * MD4File(); +-char * MD4Data(); + +-#endif /* _MD4_H_ */ ++extern void MD4Init TAC_ARGS((MD4_CTX *context)); ++extern void MD4Update TAC_ARGS((MD4_CTX *context, const unsigned char *input, unsigned int inputLen)); ++extern void MD4Final TAC_ARGS((unsigned char digest[16], MD4_CTX *context)); ++ ++ ++#endif /* MSCHAP */ ++ ++#endif /* MD4_H */ +diff --git a/md5.c b/md5.c +index 06225b0..32ca404 100644 +--- a/md5.c ++++ b/md5.c +@@ -47,12 +47,20 @@ + * to contain all the information that RFC 1321's global.h contains. + */ + ++ ++#include "tac_plus.h" ++ + #include "md5.h" + ++ ++static void MD5Transform TAC_ARGS((UINT4 *state, unsigned char *block)); ++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len)); ++static void Decode TAC_ARGS((UINT4 *output, unsigned char *input, unsigned int len)); ++ ++ + /* Constants for MD5Transform routine. + */ + +- + #define S11 7 + #define S12 12 + #define S13 17 +@@ -70,24 +78,6 @@ + #define S43 15 + #define S44 21 + +-static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64])); +-static void Encode PROTO_LIST +- ((unsigned char *, UINT4 *, unsigned int)); +-static void Decode PROTO_LIST +- ((UINT4 *, unsigned char *, unsigned int)); +- +-#if !defined(MD5_NEED_MEM_FUNCS) +- +-#define MD5_memcpy(out,in,len) memcpy(out, in, len) +-#define MD5_memset(ptr,val,len) memset(ptr, val, len) +- +-#else /* !defined(MD5_NEED_MEM_FUNCS) */ +- +-static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int)); +-static void MD5_memset PROTO_LIST((POINTER, int, unsigned int)); +- +-#endif /* !defined(MD5_NEED_MEM_FUNCS) */ +- + static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +@@ -129,6 +119,8 @@ Rotation is separate from addition to prevent recomputation. + (a) += (b); \ + } + ++void MD5Init TAC_ARGS((MD5_CTX *context)); ++ + /* MD5 initialization. Begins an MD5 operation, writing a new context. + */ + void +@@ -143,6 +135,8 @@ MD5_CTX *context; /* context */ + context->state[3] = 0x10325476; + } + ++void MD5Update TAC_ARGS((MD5_CTX *context, unsigned char *input, unsigned int inputLen)); ++ + /* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. +@@ -168,8 +162,7 @@ unsigned int inputLen; /* length of input block */ + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { +- MD5_memcpy +- ((POINTER) & context->buffer[index], (POINTER) input, partLen); ++ memcpy((POINTER) & context->buffer[index], (POINTER) input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) +@@ -180,11 +173,12 @@ unsigned int inputLen; /* length of input block */ + i = 0; + + /* Buffer remaining input */ +- MD5_memcpy +- ((POINTER) & context->buffer[index], (POINTER) & input[i], ++ memcpy((POINTER) & context->buffer[index], (POINTER) & input[i], + inputLen - i); + } + ++void MD5Final TAC_ARGS((unsigned char *digest, MD5_CTX *context)); ++ + /* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +@@ -211,9 +205,11 @@ MD5_CTX *context; /* context */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ +- MD5_memset((POINTER) context, 0, sizeof(*context)); ++ memset((POINTER) context, 0, sizeof(*context)); + } + ++static void MD5Transform TAC_ARGS((UINT4 *state, unsigned char *block)); ++ + /* MD5 basic transformation. Transforms state based on block. + */ + static void +@@ -303,9 +299,11 @@ unsigned char block[64]; + state[3] += d; + + /* Zeroize sensitive information. */ +- MD5_memset((POINTER) x, 0, sizeof(x)); ++ memset((POINTER) x, 0, sizeof(x)); + } + ++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len)); ++ + /* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +@@ -325,6 +323,8 @@ unsigned int len; + } + } + ++static void Decode TAC_ARGS((UINT4 *output, unsigned char *input, unsigned int len)); ++ + /* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +@@ -340,36 +340,3 @@ unsigned int len; + output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) | + (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24); + } +- +-#if defined(MD5_NEED_MEM_FUNC) +- +-/* Note: Replace "for loop" with standard memcpy if possible. +- */ +-static void +-MD5_memcpy(output, input, len) +-POINTER output; +-POINTER input; +-unsigned int len; +-{ +- unsigned int i; +- +- for (i = 0; i < len; i++) +- output[i] = input[i]; +-} +- +- +-/* Note: Replace "for loop" with standard memset if possible. +- */ +-static void +-MD5_memset(output, value, len) +-POINTER output; +-int value; +-unsigned int len; +-{ +- unsigned int i; +- +- for (i = 0; i < len; i++) +- ((char *) output)[i] = (char) value; +-} +- +-#endif /* defined(MD5_NEED_MEM_FUNC) */ +diff --git a/md5.h b/md5.h +index 097156e..90f9021 100644 +--- a/md5.h ++++ b/md5.h +@@ -1,3 +1,8 @@ ++#ifndef MD5_H ++#define MD5_H 1 ++ ++#include "tac_plus.h" ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -44,8 +49,6 @@ + * documentation and/or software. + */ + +-#ifndef _MD5_H +-#define _MD5_H + + /* delineate the cisco changes to the RSA supplied module */ + #define CISCO_MD5_MODS +@@ -53,16 +56,11 @@ + #if defined(CISCO_MD5_MODS) + + /* typedef a 32-bit type */ +-typedef unsigned long int UINT4; ++typedef tac_uint32 UINT4; + + /* typedef a generic pointer type */ + typedef unsigned char *POINTER; + +-/* enable prototyping */ +-/* #define PROTO_LIST(x) x */ +-/* disable prototyping */ +-#define PROTO_LIST(x) () +- + #endif /* defined(CISCO_MD5_MODS) */ + + /* MD5 context. */ +@@ -72,10 +70,10 @@ typedef struct { + unsigned char buffer[64]; /* input buffer */ + } MD5_CTX; + +-void MD5Init PROTO_LIST ((MD5_CTX *)); +-void MD5Update PROTO_LIST +- ((MD5_CTX *, unsigned char *, unsigned int)); +-void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); ++ ++extern void MD5Init TAC_ARGS((MD5_CTX *context)); ++extern void MD5Update TAC_ARGS((MD5_CTX *context, unsigned char *input, unsigned int inputLen)); ++extern void MD5Final TAC_ARGS((unsigned char *digest, MD5_CTX *context)); + + +-#endif /* _MD5_H */ ++#endif /* MD5_H */ +diff --git a/mschap.h b/mschap.h +index 192d9d2..d98b84b 100644 +--- a/mschap.h ++++ b/mschap.h +@@ -1,3 +1,8 @@ ++#ifndef MSCHAP_H ++#define MSCHAP_H 1 ++ ++#include "tac_plus.h" ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -17,4 +22,8 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #define MSCHAP_KEY "Contact Microsoft for the MSCHAP key" ++ ++ ++#endif /* MSCHAP_H */ +diff --git a/packet.c b/packet.c +index f3f7023..2e003d1 100644 +--- a/packet.c ++++ b/packet.c +@@ -17,14 +17,51 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #include "tac_plus.h" + ++#include ++#include /* for ntohl() */ ++#include ++#include ++#ifdef HAVE_SYS_TIME_H ++#include ++#endif ++#include ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++ ++#include "packet.h" ++#include "utils.h" ++#include "report.h" ++#include "dump.h" ++#include "cfgfile.h" ++#include "encrypt.h" ++#include "main.h" ++#include "do_author.h" ++ ++ ++static int write_packet TAC_ARGS((u_char *pak)); ++ ++ ++/* Configurable: ++ */ ++ ++#define TAC_PLUS_READ_TIMEOUT 180 /* seconds */ ++#define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */ ++ ++ + /* Everything to do with reading and writing packets */ + ++void send_acct_reply TAC_ARGS((unsigned status, const char *msg, const char *data)); ++ + /* send an accounting response packet */ ++void + send_acct_reply(status, msg, data) +- u_char status; +- char *msg, *data; ++unsigned status; /* promoted "u_char" type */ ++const char *msg; ++const char *data; + { + u_char *pak, *p; + HDR *hdr; +@@ -73,13 +110,16 @@ send_acct_reply(status, msg, data) + free(pak); + } + ++void send_author_reply TAC_ARGS((unsigned status, const char *msg, const char *data, int arg_cnt, /* const */ char **args)); ++ + /* send an authorization reply packet */ ++void + send_author_reply(status, msg, data, arg_cnt, args) +-u_char status; +-char *msg; +-char *data; ++unsigned status; /* promoted "u_char" type */ ++const char *msg; ++const char *data; + int arg_cnt; +-char **args; ++/* const */ char **args; + { + u_char *pak, *p; + HDR *hdr; +@@ -159,8 +199,11 @@ char **args; + /* Send an authentication reply packet indicating an error has + occurred. msg is a null terminated character string */ + ++void send_authen_error TAC_ARGS((const char *msg)); ++ ++void + send_authen_error(msg) +-char *msg; ++const char *msg; + { + char buf[255]; + +@@ -176,13 +219,16 @@ char *msg; + + /* create and send an authentication reply packet from tacacs+ to a NAS */ + ++void send_authen_reply TAC_ARGS((int status, const char *msg, unsigned msg_len, const unsigned char *data, unsigned data_len, unsigned flags)); ++ ++void + send_authen_reply(status, msg, msg_len, data, data_len, flags) + int status; +-char *msg; +-u_short msg_len; +-char *data; +-u_short data_len; +-u_char flags; ++const char *msg; ++unsigned msg_len; /* promoted "u_short" type */ ++const unsigned char *data; ++unsigned data_len; /* promoted "u_short" type */ ++unsigned flags; /* promoted "u_char" type */ + { + u_char *pak, *p; + HDR *hdr; +@@ -228,12 +274,14 @@ u_char flags; + } + + ++u_char *get_authen_continue TAC_ARGS((void)); ++ + /* read an authentication GETDATA packet from a NAS. Return 0 on failure */ + u_char * + get_authen_continue() + { + HDR *hdr; +- u_char *pak, *read_packet(); ++ u_char *pak; + struct authen_cont *cont; + char msg[255]; + +@@ -255,10 +303,10 @@ get_authen_continue() + cont->user_msg_len = ntohs(cont->user_msg_len); + cont->user_data_len = ntohs(cont->user_data_len); + +- if (TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + ++ if ((unsigned long)(TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + + cont->user_msg_len + +- cont->user_data_len != +- ntohl(hdr->datalength)) { ++ cont->user_data_len) != ++ (unsigned long) ntohl(hdr->datalength)) { + char *m = "Illegally sized authentication cont packet"; + report(LOG_ERR, "%s: %s", session.peer, m); + send_authen_error(m); +@@ -278,7 +326,9 @@ get_authen_continue() + * Return -1 on error, eof or timeout. Otherwise return number of + * bytes read. */ + +-int ++static int sockread TAC_ARGS((int fd, u_char *ptr, int nbytes, int timeout)); ++ ++static int + sockread(fd, ptr, nbytes, timeout) + int fd; + u_char *ptr; +@@ -354,7 +404,9 @@ int timeout; + * Return -1 on error, eof or timeout. Otherwise return number of + * bytes written. */ + +-int ++static int sockwrite TAC_ARGS((int fd, u_char *ptr, int bytes, int timeout)); ++ ++static int + sockwrite(fd, ptr, bytes, timeout) + int fd; + u_char *ptr; +@@ -416,16 +468,32 @@ int timeout; + return (bytes - remaining); + } + ++static const char *get_session_key TAC_ARGS((void)); ++ ++static const char * ++get_session_key() ++{ ++ const char *retval = NULL; ++ ++ if ((retval = cfg_get_host_key(session.peer_addr))) ++ return (retval); ++ if (session.peer_addr != session.peer ++ && (retval = cfg_get_host_key(session.peer ))) ++ return (retval); ++ return (session.key); ++} ++ + /* read a packet from the wire, and decrypt it. Increment the global + seq_no return NULL on failure */ + ++u_char *read_packet TAC_ARGS((void)); ++ + u_char * + read_packet() + { + HDR hdr; + u_char *pkt, *data; + int len; +- char *tkey; + + if (debug & DEBUG_PACKET_FLAG) + report(LOG_DEBUG, "Waiting for packet"); +@@ -451,7 +519,7 @@ read_packet() + len < TAC_PLUS_HDR_SIZE || len > 0x10000) { + report(LOG_ERR, + "%s: Illegal data size: %lu\n", +- session.peer, ntohl(hdr.datalength)); ++ session.peer, (unsigned long) ntohl(hdr.datalength)); + return(NULL); + } + pkt = (u_char *) tac_malloc(len); +@@ -463,9 +531,9 @@ read_packet() + data = pkt + TAC_PLUS_HDR_SIZE; + + /* read the rest of the packet data */ +- if (sockread(session.sock, data, ntohl(hdr.datalength), ++ if ((unsigned long)sockread(session.sock, data, ntohl(hdr.datalength), + TAC_PLUS_READ_TIMEOUT) != +- ntohl(hdr.datalength)) { ++ (unsigned long) ntohl(hdr.datalength)) { + report(LOG_ERR, "%s: start_session: bad socket read", session.peer); + return (NULL); + } +@@ -480,10 +548,7 @@ read_packet() + } + + /* decrypt the data portion */ +- if ( !(tkey=(char *)cfg_get_host_key(session.peer)) ) +- tkey = session.key; +- +- if (md5_xor((HDR *)pkt, data, tkey)) { ++ if (md5_xor((HDR *)pkt, data, get_session_key())) { + report(LOG_ERR, "%s: start_session error decrypting data", + session.peer); + return (NULL); +@@ -498,14 +563,16 @@ read_packet() + return (pkt); + } + ++static int write_packet TAC_ARGS((u_char *pak)); ++ + /* write a packet to the wire, encrypting it */ ++static int + write_packet(pak) + u_char *pak; + { + HDR *hdr = (HDR *) pak; + u_char *data; + int len; +- char *tkey; + + len = TAC_PLUS_HDR_SIZE + ntohl(hdr->datalength); + +@@ -513,10 +580,7 @@ u_char *pak; + data = pak + TAC_PLUS_HDR_SIZE; + + /* encrypt the data portion */ +- if ( !(tkey=(char *)cfg_get_host_key(session.peer)) ) +- tkey = session.key; +- +- if (md5_xor((HDR *)pak, data, tkey)) { ++ if (md5_xor((HDR *)pak, data, get_session_key())) { + report(LOG_ERR, "%s: write_packet: error encrypting data", session.peer); + return (-1); + } +@@ -528,6 +592,9 @@ u_char *pak; + return (0); + } + ++void send_error_reply TAC_ARGS((int type, char *msg)); ++ ++void + send_error_reply(type, msg) + int type; + char *msg; +diff --git a/packet.h b/packet.h +new file mode 100644 +index 0000000..83a4d9c +--- /dev/null ++++ b/packet.h +@@ -0,0 +1,217 @@ ++#ifndef PACKET_H ++#define PACKET_H 1 ++ ++#include "tac_plus.h" ++ ++#include /* for u_* */ ++ ++ ++/* All tacacs+ packets have the same header format */ ++ ++struct tac_plus_pak_hdr { ++ u_char version; ++ ++#define TAC_PLUS_MAJOR_VER_MASK 0xf0 ++#define TAC_PLUS_MAJOR_VER 0xc0 ++ ++#define TAC_PLUS_MINOR_VER_0 0x0 ++#define TAC_PLUS_VER_0 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_0) ++ ++#define TAC_PLUS_MINOR_VER_1 0x01 ++#define TAC_PLUS_VER_1 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_1) ++ ++ u_char type; ++ ++#define TAC_PLUS_AUTHEN 1 ++#define TAC_PLUS_AUTHOR 2 ++#define TAC_PLUS_ACCT 3 ++ ++ u_char seq_no; /* packet sequence number */ ++ u_char encryption; /* packet is encrypted or cleartext */ ++ ++#define TAC_PLUS_ENCRYPTED 0x0 /* packet is encrypted */ ++#define TAC_PLUS_CLEAR 0x1 /* packet is not encrypted */ ++ ++ int session_id; /* session identifier FIXME: Is this needed? */ ++ int datalength; /* length of encrypted data following this ++ * header */ ++ /* datalength bytes of encrypted data */ ++}; ++ ++#define TAC_PLUS_HDR_SIZE 12 ++ ++typedef struct tac_plus_pak_hdr HDR; ++ ++/* Authentication packet NAS sends to us */ ++ ++struct authen_start { ++ u_char action; ++ ++#define TAC_PLUS_AUTHEN_LOGIN 0x1 ++#define TAC_PLUS_AUTHEN_CHPASS 0x2 ++#define TAC_PLUS_AUTHEN_SENDPASS 0x3 /* deprecated */ ++#define TAC_PLUS_AUTHEN_SENDAUTH 0x4 ++ ++ u_char priv_lvl; ++ ++#define TAC_PLUS_PRIV_LVL_MIN 0x0 ++#define TAC_PLUS_PRIV_LVL_MAX 0xf ++ ++ u_char authen_type; ++ ++#define TAC_PLUS_AUTHEN_TYPE_ASCII 1 ++#define TAC_PLUS_AUTHEN_TYPE_PAP 2 ++#define TAC_PLUS_AUTHEN_TYPE_CHAP 3 ++#define TAC_PLUS_AUTHEN_TYPE_ARAP 4 ++#ifdef MSCHAP ++#define TAC_PLUS_AUTHEN_TYPE_MSCHAP 5 ++#endif /* MSCHAP */ ++ ++ u_char service; ++ ++#define TAC_PLUS_AUTHEN_SVC_LOGIN 1 ++#define TAC_PLUS_AUTHEN_SVC_ENABLE 2 ++#define TAC_PLUS_AUTHEN_SVC_PPP 3 ++#define TAC_PLUS_AUTHEN_SVC_ARAP 4 ++#define TAC_PLUS_AUTHEN_SVC_PT 5 ++#define TAC_PLUS_AUTHEN_SVC_RCMD 6 ++#define TAC_PLUS_AUTHEN_SVC_X25 7 ++#define TAC_PLUS_AUTHEN_SVC_NASI 8 ++ ++ u_char user_len; ++ u_char port_len; ++ u_char rem_addr_len; ++ u_char data_len; ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++}; ++ ++#define TAC_AUTHEN_START_FIXED_FIELDS_SIZE 8 ++ ++/* Authentication continue packet NAS sends to us */ ++struct authen_cont { ++ u_short user_msg_len; ++ u_short user_data_len; ++ u_char flags; ++ ++#define TAC_PLUS_CONTINUE_FLAG_ABORT 0x1 ++ ++ /* */ ++ /* */ ++}; ++ ++#define TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE 5 ++ ++/* Authentication reply packet we send to NAS */ ++struct authen_reply { ++ u_char status; ++ ++#define TAC_PLUS_AUTHEN_STATUS_PASS 1 ++#define TAC_PLUS_AUTHEN_STATUS_FAIL 2 ++#define TAC_PLUS_AUTHEN_STATUS_GETDATA 3 ++#define TAC_PLUS_AUTHEN_STATUS_GETUSER 4 ++#define TAC_PLUS_AUTHEN_STATUS_GETPASS 5 ++#define TAC_PLUS_AUTHEN_STATUS_RESTART 6 ++#define TAC_PLUS_AUTHEN_STATUS_ERROR 7 ++#define TAC_PLUS_AUTHEN_STATUS_FOLLOW 0x21 ++ ++ u_char flags; ++ ++#define TAC_PLUS_AUTHEN_FLAG_NOECHO 0x1 ++ ++ u_short msg_len; ++ u_short data_len; ++ ++ /* */ ++ /* */ ++}; ++ ++#define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6 ++ ++/* An authorization request packet */ ++struct author { ++ u_char authen_method; ++ u_char priv_lvl; ++ u_char authen_type; ++ u_char service; ++ ++ u_char user_len; ++ u_char port_len; ++ u_char rem_addr_len; ++ u_char arg_cnt; /* the number of args */ ++ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++}; ++ ++#define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8 ++ ++/* An authorization reply packet */ ++struct author_reply { ++ u_char status; ++ u_char arg_cnt; ++ u_short msg_len; ++ u_short data_len; ++ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++}; ++ ++#define TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE 6 ++ ++struct acct { ++ u_char flags; ++ ++#define TAC_PLUS_ACCT_FLAG_MORE 0x1 ++#define TAC_PLUS_ACCT_FLAG_START 0x2 ++#define TAC_PLUS_ACCT_FLAG_STOP 0x4 ++#define TAC_PLUS_ACCT_FLAG_WATCHDOG 0x8 ++ ++ u_char authen_method; ++ u_char priv_lvl; ++ u_char authen_type; ++ u_char authen_service; ++ u_char user_len; ++ u_char port_len; ++ u_char rem_addr_len; ++ u_char arg_cnt; /* the number of cmd args */ ++ /* one u_char containing size for each arg */ ++ /* */ ++ /* */ ++ /* */ ++ /* char data for args 1 ... n */ ++}; ++ ++#define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9 ++ ++struct acct_reply { ++ u_short msg_len; ++ u_short data_len; ++ u_char status; ++ ++#define TAC_PLUS_ACCT_STATUS_SUCCESS 0x1 ++#define TAC_PLUS_ACCT_STATUS_ERROR 0x2 ++#define TAC_PLUS_ACCT_STATUS_FOLLOW 0x21 ++ ++}; ++ ++#define TAC_ACCT_REPLY_FIXED_FIELDS_SIZE 5 ++ ++ ++extern void send_acct_reply TAC_ARGS((unsigned status, const char *msg, const char *data)); ++extern void send_author_reply TAC_ARGS((unsigned status, const char *msg, const char *data, int arg_cnt, /* const */ char **args)); ++extern void send_authen_error TAC_ARGS((const char *msg)); ++extern void send_authen_reply TAC_ARGS((int status, const char *msg, unsigned msg_len, const unsigned char *data, unsigned data_len, unsigned flags)); ++extern u_char *get_authen_continue TAC_ARGS((void)); ++extern u_char *read_packet TAC_ARGS((void)); ++extern void send_error_reply TAC_ARGS((int type, char *msg)); ++ ++ ++#endif /* PACKET_H */ +diff --git a/parse.c b/parse.c +index 6ac6908..b9f3360 100644 +--- a/parse.c ++++ b/parse.c +@@ -19,8 +19,15 @@ + + /* Keywords of the configuration language */ + ++ + #include "tac_plus.h" + ++#include "parse.h" ++#include "utils.h" ++#include "report.h" ++#include "hash.h" ++ ++ + static void *wordtable[HASH_TAB_SIZE]; /* Table of keyword declarations */ + + struct keyword { +@@ -31,6 +38,8 @@ struct keyword { + + typedef struct keyword KEYWORD; + ++static void declare TAC_ARGS((char *name, int value)); ++ + static void + declare(name, value) + char *name; +@@ -53,6 +62,8 @@ declare(name, value) + + /* Declare keywords of the "configuration language". */ + ++void parser_init TAC_ARGS((void)); ++ + void + parser_init() + { +@@ -85,7 +96,6 @@ parser_init() + declare("group", S_group); + declare("global", S_global); + declare("host", S_host); +- declare("type", S_type); + declare("ip", S_ip); + declare("ipx", S_ipx); + declare("key", S_key); +@@ -115,12 +125,23 @@ parser_init() + declare("service", S_svc); + declare("user", S_user); + declare("time", S_time); ++ declare("and", S_and); ++ declare("closeparen", S_closeparen); ++ declare("enlist", S_enlist); ++ declare("first", S_first); ++ declare("not", S_not); ++ declare("openparen", S_openparen); ++ declare("or", S_or); ++ declare("recursive", S_recursive); ++ declare("when", S_when); + } + ++int keycode TAC_ARGS((const char *keyword)); ++ + /* Return a keyword code if a keyword is recognized. 0 otherwise */ + int + keycode(keyword) +-char *keyword; ++const char *keyword; + { + KEYWORD *k = hash_lookup(wordtable, keyword); + +@@ -129,7 +150,9 @@ char *keyword; + return (S_unknown); + } + +-char * ++const char *codestring TAC_ARGS((int type)); ++ ++const char * + codestring(type) + int type; + { +@@ -156,8 +179,6 @@ int type; + return ("group"); + case S_host: + return ("host"); +- case S_type: +- return ("type"); + case S_file: + return ("file"); + case S_skey: +@@ -250,5 +271,23 @@ int type; + return("lcp"); + case S_time: + return("time"); ++ case S_and: ++ return("and"); ++ case S_closeparen: ++ return(")"); ++ case S_enlist: ++ return("enlist"); ++ case S_first: ++ return("first"); ++ case S_not: ++ return("not"); ++ case S_openparen: ++ return("("); ++ case S_or: ++ return("or"); ++ case S_recursive: ++ return("recursive"); ++ case S_when: ++ return("when"); + } + } +diff --git a/parse.h b/parse.h +index e5be7f7..34d72ee 100644 +--- a/parse.h ++++ b/parse.h +@@ -1,3 +1,8 @@ ++#ifndef PARSE_H ++#define PARSE_H 1 ++ ++#include "tac_plus.h" ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -17,8 +22,6 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + +-/* Dummy password, if nopasswd is specified */ +-extern char *nopasswd_str; + + /* Keywords & values */ + +@@ -82,9 +85,24 @@ extern char *nopasswd_str; + #define S_db 44 + #define S_db_accounting 45 + #endif /*DB*/ +-#define S_type 46 + #ifdef USE_LDAP + #define S_ldap 47 + #endif /* LDAP */ + #define S_time 48 ++#define S_and 49 ++#define S_closeparen 50 ++#define S_enlist 51 ++#define S_first 52 ++#define S_not 53 ++#define S_openparen 54 ++#define S_or 55 ++#define S_recursive 56 ++#define S_when 57 ++ ++ ++extern void parser_init TAC_ARGS((void)); ++extern int keycode TAC_ARGS((const char *keyword)); ++extern const char *codestring TAC_ARGS((int type)); ++ + ++#endif /* PARSE_H */ +diff --git a/programs.c b/programs.c +index bce0178..ac42b23 100644 +--- a/programs.c ++++ b/programs.c +@@ -19,10 +19,29 @@ + + /* Routines to fork children and communicate with them via pipes */ + ++ + #include "tac_plus.h" +-#include "sys/wait.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_UNISTD_H + #include +-#include "signal.h" ++#endif ++#include ++#ifdef HAVE_SYSLOG_H ++#include ++#endif ++#ifdef HAVE_SYS_SYSLOG_H ++#include ++#endif ++ ++#include "programs.h" ++#include "utils.h" ++#include "report.h" ++#include "do_author.h" /* for "struct author_data" */ ++#include "main.h" ++ + + /* Support for dollar variables. Look in the authorization data and + return strings representing values found there. If not found, return +@@ -38,6 +57,8 @@ type -- (1 to 4) + service -- (1 to 7) + status -- (pass, fail, error, unknown) */ + ++static char *lookup TAC_ARGS((char *sym, struct author_data *data)); ++ + static char * + lookup(sym, data) + char *sym; +@@ -96,12 +117,14 @@ struct author_data *data; + values for the various $ variables by looking in the authorization + data */ + ++static char *substitute TAC_ARGS((const char *string, struct author_data *data)); ++ + static char * + substitute(string, data) +-char *string; ++const char *string; + struct author_data *data; + { +- char *cp; ++ const char *cp; + char out[MAX_INPUT_LINE_LEN], *outp; + char sym[MAX_INPUT_LINE_LEN], *symp; + char *value, *valuep; +@@ -129,7 +152,7 @@ struct author_data *data; + + } else { + /* copy symbol into sym */ +- while (*cp && isalpha(*cp)) ++ while (*cp && isalpha((int) *cp)) + *symp++ = *cp++; + } + +@@ -160,6 +183,8 @@ struct author_data *data; + /* Wait for a (child) pid to terminate. Return its status. Probably + horribly implementation dependent. */ + ++static int waitfor TAC_ARGS((int pid)); ++ + static int + waitfor(pid) + int pid; +@@ -189,6 +214,8 @@ int pid; + return (WEXITSTATUS(status)); + } + ++static int write_args TAC_ARGS((int fd, char **args, int arg_cnt)); ++ + /* Write an argv array of strings to fd, adding a newline to each one */ + static int + write_args(fd, args, arg_cnt) +@@ -211,6 +238,8 @@ char **args; + return (0); + } + ++static void close_fds TAC_ARGS((int fd1, int fd2, int fd3)); ++ + /* Close the three given file-descruptors */ + static void + close_fds(fd1, fd2, fd3) +@@ -230,6 +259,8 @@ close_fds(fd1, fd2, fd3) + /* Fork a command. Return read and write file descriptors in readfdp + and writefdp. Return the pid or -1 if unsuccessful */ + ++static int my_popen TAC_ARGS((char *cmd, int *readfdp, int *writefdp, int *errorfdp)); ++ + static int + my_popen(cmd, readfdp, writefdp, errorfdp) + char *cmd; +@@ -297,6 +328,8 @@ int *readfdp, *writefdp, *errorfdp; + return(0); /* keep Codecenter quiet */ + } + ++static int read_string TAC_ARGS((int fd, char *string, int len)); ++ + /* read the file descriptor and stuff the data into the given array for + * the number of bytes given. Throw the rest away. + */ +@@ -305,7 +338,7 @@ read_string (fd, string, len) + int fd, len; + char *string; + { +- uint i, ret; ++ int i, ret; + char c; + + i=0; +@@ -324,6 +357,8 @@ char *string; + the count of lines seen so far. When eof is read, the array is + allocated, and the recursion unravels */ + ++static char **read_args TAC_ARGS((int n, int fd)); ++ + static char ** + read_args(n, fd) + int n, fd; +@@ -356,12 +391,16 @@ int n, fd; + standard input and read its standard output into outarray. Return + the commands final status when it terminates */ + ++int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len)); ++ + int + call_pre_process(string, data, outargsp, outargs_cntp, error, err_len) +-char *string, *error; ++const char *string; + struct author_data *data; + char ***outargsp; +-int *outargs_cntp, err_len; ++int *outargs_cntp; ++char *error; ++int err_len; + { + char **new_args; + int readfd, writefd, errorfd; +@@ -402,8 +441,8 @@ int *outargs_cntp, err_len; + + read_string(errorfd, error, err_len); + if (error[0] != '\0') { +- report(LOG_ERR, "Error from program (%d): \"%s\" ", +- strlen(error), error); ++ report(LOG_ERR, "Error from program (%u): \"%s\" ", ++ (unsigned) strlen(error), error); + } + + /* count the args */ +@@ -422,9 +461,11 @@ int *outargs_cntp, err_len; + standard input and read its standard output into outarray. Return + the commands final status when it terminates */ + ++int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp)); ++ + int + call_post_process(string, data, outargsp, outargs_cntp) +-char *string; ++const char *string; + struct author_data *data; + char ***outargsp; + int *outargs_cntp; +diff --git a/programs.h b/programs.h +new file mode 100644 +index 0000000..31ebef3 +--- /dev/null ++++ b/programs.h +@@ -0,0 +1,13 @@ ++#ifndef PROGRAMS_H ++#define PROGRAMS_H 1 ++ ++#include "tac_plus.h" ++ ++ ++struct author_data; ++ ++extern int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len)); ++extern int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp)); ++ ++ ++#endif /* PROGRAMS_H */ +diff --git a/pw.c b/pw.c +index 96c761e..a749262 100644 +--- a/pw.c ++++ b/pw.c +@@ -20,15 +20,26 @@ + /* Tacacs+ password lookup routine for those systems which don't have + setpwfile. Not for use on /etc/passwd files */ + ++ + #include "tac_plus.h" ++ ++#include + #include + #include + ++#include "pw.h" ++#include "report.h" ++#include "main.h" ++ ++ + static struct passwd pw_passwd; + ++struct passwd *tac_passwd_lookup TAC_ARGS((const char *name, const char *file)); ++ + struct passwd * + tac_passwd_lookup(name, file) +- char *name, *file; ++const char *name; ++const char *file; + { + FILE *passwd_fp = NULL; + +@@ -113,10 +124,12 @@ tac_passwd_lookup(name, file) + + pw_passwd.pw_name = uname; + pw_passwd.pw_passwd = password; +-#ifndef NO_PWAGE ++#ifdef HAVE_PASSWD_PW_AGE + pw_passwd.pw_age = NULL; ++#endif ++#ifdef HAVE_PASSWD_PW_COMMENT + pw_passwd.pw_comment = NULL; +-#endif /* NO_PWAGE */ ++#endif + pw_passwd.pw_gecos = gecos; + pw_passwd.pw_dir = homedir; + pw_passwd.pw_shell = shell; +diff --git a/pw.h b/pw.h +new file mode 100644 +index 0000000..44dc425 +--- /dev/null ++++ b/pw.h +@@ -0,0 +1,10 @@ ++#ifndef PW_H ++#define PW_H 1 ++ ++#include "tac_plus.h" ++ ++ ++extern struct passwd *tac_passwd_lookup TAC_ARGS((const char *name, const char *file)); ++ ++ ++#endif /* PW_H */ +diff --git a/pwlib.c b/pwlib.c +index 75f90b1..70a468e 100644 +--- a/pwlib.c ++++ b/pwlib.c +@@ -17,39 +17,61 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #include "tac_plus.h" +-#include "expire.h" +-#include "time_limit.h" ++ ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include + + #ifdef SHADOW_PASSWORDS ++#ifdef HAVE_SHADOW_H + #include + #endif ++#endif + +-#ifdef USE_PAM +-int +-tac_pam_auth(char *UserName,char *Password,struct authen_data *data,char *Service); +-#endif /* USE_PAM */ ++#include "pwlib.h" ++#include "expire.h" ++#include "time_limit.h" ++#include "report.h" ++#include "utils.h" ++#include "cfgfile.h" ++#include "pw.h" ++#include "choose_authen.h" /* for "struct authen_data" */ ++#include "packet.h" ++#include "main.h" ++#include "parse.h" + +-/* For database verification */ ++#ifdef USE_PAM ++#include "tac_pam.h" ++#endif + #ifdef DB +-int db_verify(); +-#endif /* DB */ +- +-/* For LDAP verification */ ++#include "db.h" /* For database verification */ ++#endif + #ifdef USE_LDAP +-#include "ldap.h" +-#endif /* LDAP */ ++#include "ldap_author.h" /* For LDAP verification */ ++#endif ++ ++ ++static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename)); ++ + + /* Generic password verification routines for des, file and cleartext + passwords */ + +-static int passwd_file_verify(); +- + /* Adjust data->status depending on whether a user has expired or not */ + ++void set_expiration_status TAC_ARGS((const char *exp_date, struct authen_data *data)); ++ + void + set_expiration_status(exp_date, data) +-char *exp_date; ++const char *exp_date; + struct authen_data *data; + { + int expired; +@@ -105,18 +127,18 @@ struct authen_data *data; + + Return 1 if password is valid */ + ++int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse)); ++ + int + verify(name, passwd, data, recurse) +-char *name, *passwd; ++const char *name; ++const char *passwd; + struct authen_data *data; + int recurse; + { +- char *exp_date; +- char *timestamp; +- char *cfg_passwd; +- char *p; ++ const char *exp_date, *cfg_passwd, *p, *timestamp; + +- timestamp = (char *)cfg_get_timestamp(name, recurse); ++ timestamp = cfg_get_timestamp(name, recurse); + if ( timestamp != NULL ) { + if( time_limit_process(timestamp) == 0 ) { + if ( debug & DEBUG_AUTHEN_FLAG ) +@@ -144,18 +166,18 @@ int recurse; + has been issued, attempt to use this password file */ + + if (!cfg_passwd) { +- char *file = cfg_get_authen_default(); ++ const char *file = cfg_get_authen_default(); + switch (cfg_get_authen_default_method()) { +- case (S_file): + +- if (file) { ++ case (S_file): ++ if (file) + return (passwd_file_verify(name, passwd, data, file)); +- } + break; ++ + #ifdef DB + case (S_db): + /* ugly check for database connect string */ +- if( strstr(file, "://") ){ ++ if( strstr(file, "://") ) { + if (debug & DEBUG_PASSWD_FLAG) + report(LOG_DEBUG,"%s %s: DB access to %s for user %s",session.peer, session.port, file, name); + if (!db_verify(name, passwd, file)) { +@@ -188,8 +210,8 @@ int recurse; + #ifdef USE_PAM + case (S_pam): + if (debug & DEBUG_PASSWD_FLAG) +- report(LOG_DEBUG, "PAM verify daemon %s == NAS %s", p,passwd); +- if (tac_pam_auth(name, passwd, data,file)) { ++ report(LOG_DEBUG, "PAM verify daemon [PAM] == NAS %s", passwd); ++ if (tac_pam_auth(name, passwd, data, file)) { + if (debug & DEBUG_PASSWD_FLAG) + report(LOG_DEBUG, "PAM default authentication fail"); + data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; +@@ -210,9 +232,8 @@ int recurse; + /* otherwise, we fail */ + data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; + return (0); +- + } +-} ++ } + + /* We have a configured password. Deal with it depending on its + type */ +@@ -323,11 +344,15 @@ int recurse; + return (0); + } + ++static int etc_passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data)); ++ + /* verify that this user/password is valid per /etc/passwd. +- Return 0 if invalid. */ ++ * Return 0 if invalid. ++ */ + static int + etc_passwd_file_verify(user, supplied_passwd, data) +-char *user, *supplied_passwd; ++const char *user; ++const char *supplied_passwd; + struct authen_data *data; + { + struct passwd *pw; +@@ -409,11 +434,14 @@ struct authen_data *data; + /* verify that this user/password is valid per a passwd(5) style + database. Return 0 if invalid. */ + ++static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename)); ++ + static int + passwd_file_verify(user, supplied_passwd, data, filename) +-char *user, *supplied_passwd; ++const char *user; ++const char *supplied_passwd; + struct authen_data *data; +-char *filename; ++const char *filename; + { + struct passwd *pw; + char *exp_date; +@@ -467,9 +495,12 @@ char *filename; + * return 1 if verified, 0 otherwise. + */ + ++int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd)); ++ + int + des_verify(users_passwd, encrypted_passwd) +-char *users_passwd, *encrypted_passwd; ++const char *users_passwd; ++const char *encrypted_passwd; + { + char *ep; + +diff --git a/pwlib.h b/pwlib.h +new file mode 100644 +index 0000000..92b54af +--- /dev/null ++++ b/pwlib.h +@@ -0,0 +1,14 @@ ++#ifndef PWLIB_H ++#define PWLIB_H 1 ++ ++#include "tac_plus.h" ++ ++ ++struct authen_data; ++ ++extern void set_expiration_status TAC_ARGS((const char *exp_date, struct authen_data *data)); ++extern int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse)); ++extern int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd)); ++ ++ ++#endif /* PWLIB_H */ +diff --git a/report.c b/report.c +index a458617..d46abfd 100644 +--- a/report.c ++++ b/report.c +@@ -17,20 +17,37 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #include "tac_plus.h" +-#include + +-#ifdef AIX ++#include + #include +-#else + #include ++#include ++#ifdef HAVE_FCNTL_H ++#include + #endif +- +-#ifdef __STDC__ +-#include /* ANSI C, variable length args */ +-#else +-#include /* has 'vararg' definitions */ ++#include ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++#ifdef HAVE_SYSLOG_H ++#include + #endif ++#ifdef HAVE_SYS_SYSLOG_H ++#include ++#endif ++ ++#include "report.h" ++#include "utils.h" ++#include "main.h" ++ ++ ++/* Configurable: ++ */ ++ ++#define LOGFILE_DEFAULT "/var/log/tac_plus.log" ++ + + FILE *ostream = NULL; + +@@ -45,21 +62,30 @@ char *logfile = LOGFILE_DEFAULT; + * All other priorities are always logged to syslog. + */ + ++void report TAC_ARGS((int priority, const char *fmt, ...)) G_GNUC_PRINTF(2, 3); ++ + #ifdef __STDC__ ++ ++#include /* ANSI C, variable length args */ + void +-report(int priority, char *fmt,...) +-#else ++report(int priority, const char *fmt,...) ++ ++#else /* __STDC__ */ ++ ++#include /* has 'vararg' definitions */ + /* VARARGS2 */ + void + report(priority, fmt, va_alist) + int priority; +-char *fmt; ++const char *fmt; + va_dcl /* no terminating semi-colon */ +-#endif ++ ++#endif /* __STDC__ */ + { + char msg[255]; /* temporary string */ +- char *fp, *bufp, *charp; +- int len, m, i, n; ++ const char *fp; ++ char *bufp, *charp = NULL /* GCC paranoia */; ++ int len, m = 0 /* GCC paranoia */, i, n; + char digits[16]; + va_list ap; + +@@ -119,6 +145,9 @@ va_dcl /* no terminating semi-colon */ + m = strlen(digits); + charp = digits; + break; ++ default: ++ syslog(LOG_ERR, "Unknown format character '%c', ignoring it", *fp); ++ continue; + } + + if ((len + m + 1) >= n) { +@@ -167,7 +196,7 @@ va_dcl /* no terminating semi-colon */ + + ct[24] = '\0'; + tac_lockfd(logfile, logfd); +- sprintf(buf, "%s [%d]: ", ct, getpid()); ++ sprintf(buf, "%s [%d]: ", ct, (int) getpid()); + write(logfd, buf, strlen(buf)); + if (priority == LOG_ERR) + write(logfd, "Error ", 6); +@@ -190,6 +219,8 @@ va_dcl /* no terminating semi-colon */ + syslog(priority, "%s", msg); + } + ++void report_hex TAC_ARGS((int priority, u_char *p, int len)); ++ + /* format a hex dump for syslog */ + void + report_hex(priority, p, len) +@@ -225,6 +256,8 @@ int len; + } + + ++void report_string TAC_ARGS((int priority, u_char *p, int len)); ++ + /* format a non-null terminated string for syslog */ + void + report_string(priority, p, len) +@@ -251,10 +284,11 @@ int len; + report(priority, "%s", buf); + } + ++void tac_regerror TAC_ARGS((const char *s)); ++ + void +-regerror(s) +-char *s; ++tac_regerror(s) ++const char *s; + { + report(LOG_ERR, "in regular expression %s", s); + } +- +diff --git a/report.h b/report.h +new file mode 100644 +index 0000000..5e5d1bb +--- /dev/null ++++ b/report.h +@@ -0,0 +1,26 @@ ++#ifndef REPORT_H ++#define REPORT_H 1 ++ ++#include "tac_plus.h" ++ ++#include ++#include /* for u_* */ ++#ifdef HAVE_SYSLOG_H ++#include /* for LOG_* level values */ ++#endif ++#ifdef HAVE_SYS_SYSLOG_H ++#include /* for LOG_* level values */ ++#endif ++ ++ ++extern FILE *ostream; /* for logging to console */ ++extern char *logfile; ++ ++ ++extern void report TAC_ARGS((int priority, const char *fmt, ...)) G_GNUC_PRINTF(2, 3); ++extern void report_hex TAC_ARGS((int priority, u_char *p, int len)); ++extern void report_string TAC_ARGS((int priority, u_char *p, int len)); ++extern void tac_regerror TAC_ARGS((const char *s)); ++ ++ ++#endif /* REPORT_H */ +diff --git a/sendauth.c b/sendauth.c +index 68e5482..2076dc9 100644 +--- a/sendauth.c ++++ b/sendauth.c +@@ -17,21 +17,43 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #include "tac_plus.h" ++ ++#include ++ ++#include "sendauth.h" + #include "expire.h" + #include "md5.h" ++#include "report.h" ++#include "cfgfile.h" ++#include "utils.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" + +-static int do_sendauth_fn(); +-static void outbound_chap(); + #ifdef MSCHAP +-static void outbound_mschap(); +-#endif /* MSCHAP */ +-void outbound_pap(); ++#include "default_fn.h" ++#endif ++ ++ ++static int do_sendauth_fn TAC_ARGS((struct authen_data *data)); ++static void outbound_chap TAC_ARGS((struct authen_data *data)); ++static void outbound_pap TAC_ARGS((struct authen_data *data)); ++ ++#ifdef MSCHAP ++static void outbound_mschap TAC_ARGS((struct authen_data *data)); ++#endif ++ ++ ++int sendauth_fn TAC_ARGS((struct authen_data *data)); + + int sendauth_fn(data) + struct authen_data *data; + { +- int status; ++ int retval; + char *name, *p; + + name = data->NAS_id->username; +@@ -39,8 +61,9 @@ struct authen_data *data; + if (STREQ(name, DEFAULT_USERNAME)) { + /* This username is only valid for authorization */ + data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; ++ retval = 0; + } else { +- status = do_sendauth_fn(data); ++ retval = do_sendauth_fn(data); + } + + if (debug) { +@@ -71,7 +94,7 @@ struct authen_data *data; + (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ? + "accepted" : "rejected"); + } +- return(status); ++ return (retval); + } + + /* +@@ -84,11 +107,13 @@ struct authen_data *data; + * Return 0 if data->status is valid, otherwise 1 + */ + ++static int do_sendauth_fn TAC_ARGS((struct authen_data *data)); ++ + static int + do_sendauth_fn(data) + struct authen_data *data; + { +- char *name, *exp_date; ++ const char *name, *exp_date; + + data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; + +@@ -129,11 +154,13 @@ struct authen_data *data; + return (0); + } + +-void ++static void outbound_pap TAC_ARGS((struct authen_data *data)); ++ ++static void + outbound_pap(data) + struct authen_data *data; + { +- char *secret, *p, *name; ++ const char *secret, *p, *name; + + name = data->NAS_id->username; + +@@ -163,17 +190,19 @@ struct authen_data *data; + return; + } + +- data->server_data = tac_strdup(p); +- data->server_dlen = strlen(data->server_data); ++ data->server_data = (unsigned char *) tac_strdup(p); ++ data->server_dlen = strlen((char *) data->server_data); + data->status = TAC_PLUS_AUTHEN_STATUS_PASS; + } + ++static void outbound_chap TAC_ARGS((struct authen_data *data)); ++ + static void + outbound_chap(data) + struct authen_data *data; + { +- char *name, *secret, *chal, digest[MD5_LEN]; +- char *p; ++ const char *name, *secret, *chal, *p; ++ char digest[MD5_LEN]; + u_char *mdp; + char id; + int chal_len, inlen; +@@ -262,12 +291,13 @@ struct authen_data *data; + + #ifdef MSCHAP + ++static void outbound_mschap TAC_ARGS((struct authen_data *data)); ++ + static void + outbound_mschap(data) + struct authen_data *data; + { +- char *name, *secret, *chal; +- char *p; ++ const char *name, *secret, *chal, *p; + char id; + int chal_len; + +diff --git a/sendauth.h b/sendauth.h +new file mode 100644 +index 0000000..d3e2fbc +--- /dev/null ++++ b/sendauth.h +@@ -0,0 +1,12 @@ ++#ifndef SENDAUTH_H ++#define SENDAUTH_H 1 ++ ++#include "tac_plus.h" ++ ++ ++struct authen_data; ++ ++extern int sendauth_fn TAC_ARGS((struct authen_data *data)); ++ ++ ++#endif /* SENDAUTH_H */ +diff --git a/sendpass.c b/sendpass.c +index f7f0b3c..9d8ab00 100644 +--- a/sendpass.c ++++ b/sendpass.c +@@ -17,11 +17,24 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + #include "tac_plus.h" ++ ++#include "sendpass.h" + #include "expire.h" ++#include "report.h" ++#include "utils.h" ++#include "cfgfile.h" ++#include "choose_authen.h" /* for "struct authen_data" */ ++#include "do_author.h" /* for "struct identity" */ ++#include "main.h" ++#include "packet.h" + +-static int +-do_sendpass_fn(); ++ ++static int do_sendpass_fn TAC_ARGS((struct authen_data *data)); ++ ++ ++int sendpass_fn TAC_ARGS((struct authen_data *data)); + + int sendpass_fn(data) + struct authen_data *data; +@@ -63,17 +76,17 @@ struct authen_data *data; + * Any strings pointed to by authen_data must come from the heap. They + * will get freed by the caller. + * +- * Return 0 if data->status is valid, otherwise 1 */ ++ * Return 0 if data->status is valid, otherwise 1 ++ */ ++ ++static int do_sendpass_fn TAC_ARGS((struct authen_data *data)); + + static int + do_sendpass_fn(data) + struct authen_data *data; + { +- char *name; +- char *p; ++ const char *name, *exp_date, *secret, *p; + int expired; +- char *exp_date; +- char *secret; + + data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; + +@@ -159,8 +172,8 @@ struct authen_data *data; + return(0); + } + +- data->server_data = tac_strdup(p); +- data->server_dlen = strlen(data->server_data); ++ data->server_data = (unsigned char *) tac_strdup(p); ++ data->server_dlen = strlen((char *) data->server_data); + data->status = TAC_PLUS_AUTHEN_STATUS_PASS; + if (expired == PW_EXPIRING) { + data->server_msg = tac_strdup("Secret will expire soon"); +diff --git a/sendpass.h b/sendpass.h +new file mode 100644 +index 0000000..7481cff +--- /dev/null ++++ b/sendpass.h +@@ -0,0 +1,12 @@ ++#ifndef SENDPASS_H ++#define SENDPASS_H 1 ++ ++#include "tac_plus.h" ++ ++ ++struct authen_data; ++ ++extern int sendpass_fn TAC_ARGS((struct authen_data *data)); ++ ++ ++#endif /* SENDPASS_H */ +diff --git a/skey_fn.c b/skey_fn.c +index d3c9860..0a74fa8 100644 +--- a/skey_fn.c ++++ b/skey_fn.c +@@ -17,17 +17,22 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + +-#ifdef SKEY ++ + #include "tac_plus.h" ++ ++#ifdef SKEY ++ ++#include ++ ++#include "skey_fn.h" + #include "expire.h" + ++ + /* internal state variables */ + #define STATE_AUTHEN_START 0 /* no requests issued */ + #define STATE_AUTHEN_GETUSER 1 /* username has been requested */ + #define STATE_AUTHEN_GETPASS 2 /* password has been requested */ + +-#include +- + struct private_data { + struct skey skey; + char password[MAX_PASSWD_LEN + 1]; +@@ -37,6 +42,8 @@ struct private_data { + /* Use s/key to verify a supplied password using state set up earlier + when the username was supplied */ + ++static int skey_verify TAC_ARGS((char *passwd, struct authen_data *data)); ++ + static int + skey_verify(passwd, data) + char *passwd; +@@ -73,6 +80,8 @@ struct authen_data *data; + * Return 0 if data->status is valid, otherwise 1 + */ + ++int skey_fn TAC_ARGS((struct authen_data *data)); ++ + int + skey_fn(data) + struct authen_data *data; +@@ -222,12 +231,9 @@ struct authen_data *data; + return (1); + } + } +-#else /* SKEY */ + +-/* The following code is not needed or used. It exists solely to +- prevent compilers from "helpfully" complaining that this source +- file is empty, which upsets novices building the software */ ++#else /* SKEY */ + +-static int dummy = 0; ++TAC_SOURCEFILE_EMPTY + + #endif /* SKEY */ +diff --git a/skey_fn.h b/skey_fn.h +new file mode 100644 +index 0000000..1b7474a +--- /dev/null ++++ b/skey_fn.h +@@ -0,0 +1,14 @@ ++#ifndef SKEY_FN_H ++#define SKEY_FN_H 1 ++ ++#include "tac_plus.h" ++ ++#ifdef SKEY ++ ++ ++extern int skey_fn TAC_ARGS((struct authen_data *data)); ++ ++ ++#endif /* SKEY */ ++ ++#endif /* SKEY_FN_H */ +diff --git a/tac_pam.c b/tac_pam.c +index bf1b215..4f088a0 100644 +--- a/tac_pam.c ++++ b/tac_pam.c +@@ -1,12 +1,10 @@ +-#ifdef USE_PAM +- + /* tac_pam.auth.c + * A simple pam authentication routine written by + * Max Liccardo + * PAM_RUSER=username/rem_addr. + */ + +- /* ++/* + This program was contributed by Shane Watts + [modifications by AGM] + +@@ -14,23 +12,39 @@ + # check authorization + check_user auth required /usr/lib/security/pam_unix_auth.so + check_user account required /usr/lib/security/pam_unix_acct.so +- */ ++*/ ++ ++ ++#include "tac_plus.h" ++ ++#ifdef USE_PAM + + #include + #include + #include + #include +-#include "tac_plus.h" + +-typedef struct +-{ +- char *UserName; +- char *Passwd; ++#include "tac_pam.h" ++#include "report.h" ++#include "utils.h" ++#include "choose_authen.h" /* for "struct authen_data" */ ++#include "do_author.h" /* for "struct identity" */ ++#include "main.h" ++ ++ ++typedef struct { ++ const char *UserName; ++ const char *Passwd; + } UserCred; + + +-static int fconv(int num_msg, const struct pam_message **msg, +- struct pam_response **resp,void *appdata_ptr) ++static int fconv TAC_ARGS((int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)); ++ ++static int fconv(num_msg, msg, resp, appdata_ptr) ++int num_msg; ++const struct pam_message **msg; ++struct pam_response **resp; ++void *appdata_ptr; + { + int i; + UserCred *lUserCred; +@@ -38,19 +52,16 @@ static int fconv(int num_msg, const struct pam_message **msg, + + lUserCred = appdata_ptr; + +- if(lUserCred == NULL) +- { ++ if(lUserCred == NULL) { + report(LOG_ERR,"argh....maybe a SunOs 5.6 ???"); + return(PAM_CONV_ERR); + } + ++ *resp = (struct pam_response *) tac_malloc(num_msg * sizeof(struct pam_response)); + +- *resp = (struct pam_response *) calloc(num_msg,sizeof(struct pam_response)); ++ for (i=0; imsg_style) { + +- for(i=0;imsg_style) +- { + case PAM_PROMPT_ECHO_OFF: + resp[i]->resp = strdup(lUserCred->Passwd); + break; +@@ -60,6 +71,7 @@ static int fconv(int num_msg, const struct pam_message **msg, + break; + + default: ++ resp[i]->resp = NULL; + report(LOG_DEBUG,"conv default"); + break; + } +@@ -70,12 +82,16 @@ static int fconv(int num_msg, const struct pam_message **msg, + } + + +- ++int tac_pam_auth TAC_ARGS((const char *aszUserName, const char *aszPassword, struct authen_data *data, const char *aszService)); + + int +-tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char *aszService) ++tac_pam_auth(aszUserName, aszPassword, data, aszService) ++const char *aszUserName; ++const char *aszPassword; ++struct authen_data *data; ++const char *aszService; + { +- pam_handle_t *pamh=NULL; ++ pam_handle_t *pamh = NULL; + int retval; + char *lpszRemoteUser; /* Username/NAC address */ + struct pam_conv s_conv; +@@ -89,17 +105,13 @@ tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char * + s_conv.appdata_ptr = (void *) &s_UserCred; + + +- if((lpszRemoteUser = calloc(strlen(aszUserName)+strlen(data->NAS_id->NAC_address)+2,sizeof(char))) == NULL) +- { +- report(LOG_ERR,"cannot malloc"); +- return(1); +- } ++ lpszRemoteUser = tac_malloc((strlen(aszUserName)+1+strlen(data->NAS_id->NAC_address)+1) * sizeof(char)); + + retval = pam_start(aszService,aszUserName , &s_conv, &pamh); + +- if (retval != PAM_SUCCESS) +- { ++ if (retval != PAM_SUCCESS) { + report(LOG_ERR, "cannot start pam-authentication"); ++ free(lpszRemoteUser); + pamh = NULL; + return(1); + } +@@ -130,10 +142,15 @@ tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char * + * Devrim SERAL + */ + ++int tac_pam_authorization TAC_ARGS((const char *aszUserName, struct author_data *data, const char *aszService)); ++ + int +-tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszService) ++tac_pam_authorization(aszUserName, data, aszService) ++const char *aszUserName; ++struct author_data *data; ++const char *aszService; + { +- pam_handle_t *pamh=NULL; ++ pam_handle_t *pamh = NULL; + int retval; + char *lpszRemoteUser; /* Username/NAC address */ + struct pam_conv s_conv; +@@ -145,24 +162,18 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi + s_conv.conv = fconv; + s_conv.appdata_ptr = (void *) &s_UserCred; + +- if (aszService== NULL) +- { ++ if (aszService== NULL) { + report(LOG_ERR,"Service Name doesn't available So authorize him"); + return(0); + } + +- +- if((lpszRemoteUser = calloc(strlen(aszUserName)+strlen(data->id->NAC_address)+2,sizeof(char))) == NULL) +- { +- report(LOG_ERR,"cannot malloc"); +- return(1); +- } ++ lpszRemoteUser = tac_malloc((strlen(aszUserName)+strlen(data->id->NAC_address)+2) * sizeof(char)); + + retval = pam_start(aszService,aszUserName , &s_conv, &pamh); + +- if (retval != PAM_SUCCESS) +- { ++ if (retval != PAM_SUCCESS) { + report(LOG_ERR, "cannot start pam-authentication"); ++ free(lpszRemoteUser); + pamh = NULL; + return(1); + } +@@ -177,11 +188,12 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi + + retval = pam_acct_mgmt(pamh, 0); /* Is user permit to gain access system */ + +- if(retval != PAM_SUCCESS) ++ if (retval != PAM_SUCCESS) + report(LOG_ERR, "Pam Account Managment:%s",pam_strerror(pamh,retval)); +- else ++ else { + if (debug & DEBUG_AUTHOR_FLAG) + report(LOG_DEBUG, "PAM authorization allow user"); ++ } + + if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ + pamh = NULL; +@@ -191,9 +203,8 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi + return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */ + } + ++#else /* USE_PAM */ + +-#endif /* USE_PAM */ +- +- +- ++TAC_SOURCEFILE_EMPTY + ++#endif /* USE_PAM */ +diff --git a/tac_pam.h b/tac_pam.h +new file mode 100644 +index 0000000..c8306ba +--- /dev/null ++++ b/tac_pam.h +@@ -0,0 +1,18 @@ ++#ifndef TAC_PAM_H ++#define TAC_PAM_H 1 ++ ++#include "tac_plus.h" ++ ++#ifdef USE_PAM ++ ++ ++struct authen_data; ++struct author_data; ++ ++extern int tac_pam_auth TAC_ARGS((const char *aszUserName, const char *aszPassword, struct authen_data *data, const char *aszService)); ++extern int tac_pam_authorization TAC_ARGS((const char *aszUserName, struct author_data *data, const char *aszService)); ++ ++ ++#endif /* USE_PAM */ ++ ++#endif /* TAC_PAM_H */ +diff --git a/tac_plus.1 b/tac_plus.1 +index 579c7ee..69f3502 100644 +--- a/tac_plus.1 ++++ b/tac_plus.1 +@@ -111,6 +111,8 @@ the following values are recognised: + .nf + + Value Meaning ++2 config file parsing debugging ++4 process forking debugging + 8 authorisation debugging + 16 authentication debugging + 32 password file processing debugging +@@ -120,6 +122,11 @@ Value Meaning + 512 encryption/decryption + 1024 MD5 hash algorithm debugging + 2048 very low level encryption/decryption ++4096 config file memory allocation freeing ++8192 pre/post authorization program arguments substitutions ++16384 config file expressions with entity tracing ++32768 maxsess (concurrent logins) debugging ++65536 file locking progress reporting + + .fi + .TP +diff --git a/tac_plus.cfg b/tac_plus.cfg +index 31e53f5..d217258 100644 +--- a/tac_plus.cfg ++++ b/tac_plus.cfg +@@ -51,4 +51,3 @@ user = DEFAULT { + #group = staff { + # time = "Wd1800-1817|!Wd1819-2000" + #} +- +diff --git a/tac_plus.h b/tac_plus.h +index 99d95e0..1bb658f 100644 +--- a/tac_plus.h ++++ b/tac_plus.h +@@ -1,3 +1,6 @@ ++#ifndef TAC_PLUS_H ++#define TAC_PLUS_H 1 ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -16,8 +19,12 @@ + WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. + */ +-/* For autoconfig */ ++ ++ ++#ifdef HAVE_CONFIG_H + #include "config.h" ++#endif ++ + + /* + * If you are defining a system from scratch, the following may be useful. +@@ -33,25 +40,9 @@ + /* Do you need tacacs+ versions of bzero etc. */ + /* #define NEED_BZERO */ + +-/* 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 */ +-/*#define SHADOW_PASSWORDS*/ +- +-/* Define this if your malloc is defined in malloc.h instead of stdlib.h */ +-/* #define STDLIB_MALLOC */ +- + /* Define this if your wait call status is a union as opposed to an int */ + /* #define UNIONWAIT */ + +-/* Define this if your signal() uses a function returning void instead +- * of int +- */ +-/* #define VOIDSIG */ +- +-/* Define this if your password file does not contain age and comment fields. */ +-/* #define NO_PWAGE */ +- + /* Define this if you need a getdtablesize routine defined */ + /* #define GETDTABLESIZE */ + +@@ -64,23 +55,14 @@ + */ + /* #define ARAP_DES */ + +-/* Define this if you find that your daemon quits after being sent more than +- * one SIGUSR1. Some systems need to explicitly rearm signals after they've been +- * used once +- */ +-/* #define REARMSIGNAL */ +- +-#define VERSION "F4.0.3.alpha.v8 (Extended Tac_plus)" ++#define VERSION_TAIL " (Extended Tac_plus)" + + /* + * System definitions. + */ + + #ifdef NETBSD +-#define STDLIB_MALLOC +-#define NO_PWAGE + #define CONST_SYSERRLIST +-#define VOIDSIG + #endif + + #ifdef AIX +@@ -97,15 +79,10 @@ + #define _BSD 1 + #define _BSD_INCLUDES + #define UNIONWAIT +-#define NO_PWAGE + #endif /* AIX */ + + #ifdef LINUX +-#define VOIDSIG +-#define NO_PWAGE + #define REAPCHILD +-#include +-#define REARMSIGNAL + #ifdef GLIBC + #define CONST_SYSERRLIST + #endif +@@ -122,30 +99,17 @@ + #define SYSV + #define GETDTABLESIZE + #define REAPCHILD +-#define SHADOW_PASSWORDS + #define NEED_BZERO +-#define REARMSIGNAL + #endif /* SOLARIS */ + + #ifdef HPUX + #define SYSV + #define GETDTABLESIZE + #define REAPCHILD +-#define SYSLOG_IN_SYS +-#define REARMSIGNAL + #endif /* HPUX */ + + #ifdef FREEBSD + #define CONST_SYSERRLIST +-#define STDLIB_MALLOC +-#define VOIDSIG +-#define NO_PWAGE +-#endif +- +-#ifdef BSDI +-#define VOIDSIG +-#define STDLIB_MALLOC +-#define NO_PWAGE + #endif + + #define MD5_LEN 16 +@@ -153,36 +117,26 @@ + #define MSCHAP_DIGEST_LEN 49 + #endif /* MSCHAP */ + +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#ifdef SYSLOG_IN_SYS +-#include +-#else +-#include +-#endif +- +-#include +- +-#include +- + #ifdef SYSV ++#ifdef HAVE_FCNTL_H + #include ++#endif + #define index strchr +-#else /* ! SYSV */ +-#include + #endif /* SYSV */ + ++/* Sometimes are bzero/bcopy/bcmp declared as prototypes with non-void argument ++ * pointers. In such case we would generate too much warnings. ++ */ ++#include ++#ifdef HAVE_STRINGS_H ++#include ++#endif ++ ++#define bzero(s, n) bzero((void *)(s), (size_t)(n)) ++#define bcopy(src, dest, n) bcopy((const void *)(src), (void *)(dest), (size_t)(n)) ++#define bcmp(s1, s2, n) bcmp((const void *)(s1), (const void *)(s2), (size_t)(n)) ++ ++ + #ifndef TACPLUS_PIDFILE + #define TACPLUS_PIDFILE "/var/run/tac_plus.pid" + #endif +@@ -193,559 +147,94 @@ + * know what you are doing. + */ + +-#define DOLLARSIGN '$' +- +-/* +- * XTACACSP protocol defintions +- */ +- +-/* +- * 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 +- */ ++/* Stolen from pbmplus.h of netpbm: */ + +-#define AUTHEN_NAME_SIZE 128 ++#if __STDC__ ++#define TAC_ARGS(alist) alist ++#else /*__STDC__*/ ++#define TAC_ARGS(alist) () ++#endif /*__STDC__*/ + +-struct authen_type { +- char authen_name[AUTHEN_NAME_SIZE]; +- int (*authen_func)(); +- int authen_type; +-}; ++/* Stolen from glib of Gnome/GTK+ environment: */ + +-/* +- * 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. ++/* Provide macros to feature the GCC function attribute. + */ +- +-struct identity { +- char *username; +- char *NAS_name; +- char *NAS_port; +- char *NAC_address; +- int priv_lvl; +-}; +- +-/* +- * The authen_data structure is the data structure for passing +- * information to and from the authentication function +- * (authen_type.authen_func). ++#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) ++#define G_GNUC_PRINTF( format_idx, arg_idx ) \ ++ __attribute__((format (printf, format_idx, arg_idx))) ++#define G_GNUC_NORETURN \ ++ __attribute__((noreturn)) ++#define G_GNUC_CONST \ ++ __attribute__((const)) ++#define G_GNUC_UNUSED \ ++ __attribute__((unused)) ++#else /* !__GNUC__ */ ++#define G_GNUC_PRINTF( format_idx, arg_idx ) ++#define G_GNUC_NORETURN ++#define G_GNUC_CONST ++#define G_GNUC_UNUSED ++#endif /* !__GNUC__ */ ++ ++/* Provide convenience macros for handling structure ++ * fields through their offsets. ++ * Project tac_plus changed "gulong" to "glong" to be able to recover ++ * original structure base from pointer to its member. + */ ++#define G_STRUCT_OFFSET(struct_type, member) \ ++ ((long) ((char*) &((struct_type*) 0)->member)) ++#define G_STRUCT_MEMBER_P(struct_p, struct_offset) \ ++ ((void*) ((char*) (struct_p) + (long) (struct_offset))) ++#define G_STRUCT_MEMBER(member_type, struct_p, struct_offset) \ ++ (*(member_type*) G_STRUCT_MEMBER_P ((struct_p), (struct_offset))) + +-struct authen_data { +- struct identity *NAS_id; /* user identity */ +- char *server_msg; /* null-terminated output msg */ +- +- int server_dlen; /* output data length */ +- char *server_data; /* output data */ +- +- char *client_msg; /* null-terminated input msg a user typed */ ++/* reversed, in fact: for inside "member_p" calculate "struct" address */ ++#define TAC_MEMBER_STRUCT(struct_type, member_p, member) \ ++ (G_STRUCT_MEMBER(struct_type,member_p,-G_STRUCT_OFFSET(struct_type,member))) + +- 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 */ +- +- +-/* +- * This structure is the data structure for passing information to +- * and from the authorization function (do_author()). ++/* may define MAX & MIN without checking whether they ++ * were previously already defined: + */ +-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 */ +- +-}; +- +-/* 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; +-}; +- +-#ifndef TAC_PLUS_PORT +-#define TAC_PLUS_PORT 49 ++#ifdef HAVE_SYS_PARAM_H ++#include + #endif + +-/* Define tac_plus name for hosts.* files */ +-#ifdef TCPWRAPPER +-#define TACNAME "tac_plus" ++/* Provide definitions for some commonly used macros. ++ * Some of them are only provided if they haven't already ++ * been defined. It is assumed that if they are already ++ * defined then the current definition is correct. ++ */ ++#ifndef NULL ++#define NULL ((void*) 0) + #endif + +-#define TAC_PLUS_READ_TIMEOUT 180 /* seconds */ +-#define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */ +- +-#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 *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 struct session session; /* the session */ +- +-/* Global variables */ +- +-extern int debug; /* debugging flag */ +-extern int logging; /* syslog logging flag */ +-extern int single; /* do not fork (for debugging) */ +-extern int console; /* log to console */ +-extern FILE *ostream; /* for logging to console */ +-extern int parse_only; /* exit after parsing verbosely */ +-extern int sendauth_only; /* don't do sendauth */ +- +-/* All tacacs+ packets have the same header format */ +- +-struct tac_plus_pak_hdr { +- u_char version; +- +-#define TAC_PLUS_MAJOR_VER_MASK 0xf0 +-#define TAC_PLUS_MAJOR_VER 0xc0 +- +-#define TAC_PLUS_MINOR_VER_0 0x0 +-#define TAC_PLUS_VER_0 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_0) +- +-#define TAC_PLUS_MINOR_VER_1 0x01 +-#define TAC_PLUS_VER_1 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_1) +- +- u_char type; ++#undef MAX ++#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +-#define TAC_PLUS_AUTHEN 1 +-#define TAC_PLUS_AUTHOR 2 +-#define TAC_PLUS_ACCT 3 +- +- u_char seq_no; /* packet sequence number */ +- u_char encryption; /* packet is encrypted or cleartext */ +- +-#define TAC_PLUS_ENCRYPTED 0x0 /* packet is encrypted */ +-#define TAC_PLUS_CLEAR 0x1 /* packet is not encrypted */ +- +- int session_id; /* session identifier FIXME: Is this needed? */ +- int datalength; /* length of encrypted data following this +- * header */ +- /* datalength bytes of encrypted data */ +-}; +- +-#define HASH_TAB_SIZE 157 /* user and group hash table sizes */ +- +-#define TAC_PLUS_HDR_SIZE 12 +- +-typedef struct tac_plus_pak_hdr HDR; +- +-/* Authentication packet NAS sends to us */ +- +-struct authen_start { +- u_char action; +- +-#define TAC_PLUS_AUTHEN_LOGIN 0x1 +-#define TAC_PLUS_AUTHEN_CHPASS 0x2 +-#define TAC_PLUS_AUTHEN_SENDPASS 0x3 /* deprecated */ +-#define TAC_PLUS_AUTHEN_SENDAUTH 0x4 ++#undef MIN ++#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +- u_char priv_lvl; + +-#define TAC_PLUS_PRIV_LVL_MIN 0x0 +-#define TAC_PLUS_PRIV_LVL_MAX 0xf ++/* The following code is not needed or used. It exists solely to ++ prevent compilers from "helpfully" complaining that this source ++ file is empty, which upsets novices building the software */ + +- u_char authen_type; ++#define TAC_SOURCEFILE_EMPTY \ ++ static int dummy_unused G_GNUC_UNUSED = 0; + +-#define TAC_PLUS_AUTHEN_TYPE_ASCII 1 +-#define TAC_PLUS_AUTHEN_TYPE_PAP 2 +-#define TAC_PLUS_AUTHEN_TYPE_CHAP 3 +-#define TAC_PLUS_AUTHEN_TYPE_ARAP 4 +-#ifdef MSCHAP +-#define TAC_PLUS_AUTHEN_TYPE_MSCHAP 5 +-#endif /* MSCHAP */ + +- u_char service; +- +-#define TAC_PLUS_AUTHEN_SVC_LOGIN 1 +-#define TAC_PLUS_AUTHEN_SVC_ENABLE 2 +-#define TAC_PLUS_AUTHEN_SVC_PPP 3 +-#define TAC_PLUS_AUTHEN_SVC_ARAP 4 +-#define TAC_PLUS_AUTHEN_SVC_PT 5 +-#define TAC_PLUS_AUTHEN_SVC_RCMD 6 +-#define TAC_PLUS_AUTHEN_SVC_X25 7 +-#define TAC_PLUS_AUTHEN_SVC_NASI 8 +- +- u_char user_len; +- u_char port_len; +- u_char rem_addr_len; +- u_char data_len; +- /* */ +- /* */ +- /* */ +- /* */ +-}; +- +-#define TAC_AUTHEN_START_FIXED_FIELDS_SIZE 8 +- +-/* Authentication continue packet NAS sends to us */ +-struct authen_cont { +- u_short user_msg_len; +- u_short user_data_len; +- u_char flags; +- +-#define TAC_PLUS_CONTINUE_FLAG_ABORT 0x1 +- +- /* */ +- /* */ +-}; +- +-#define TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE 5 +- +-/* Authentication reply packet we send to NAS */ +-struct authen_reply { +- u_char status; +- +-#define TAC_PLUS_AUTHEN_STATUS_PASS 1 +-#define TAC_PLUS_AUTHEN_STATUS_FAIL 2 +-#define TAC_PLUS_AUTHEN_STATUS_GETDATA 3 +-#define TAC_PLUS_AUTHEN_STATUS_GETUSER 4 +-#define TAC_PLUS_AUTHEN_STATUS_GETPASS 5 +-#define TAC_PLUS_AUTHEN_STATUS_RESTART 6 +-#define TAC_PLUS_AUTHEN_STATUS_ERROR 7 +-#define TAC_PLUS_AUTHEN_STATUS_FOLLOW 0x21 +- +- u_char flags; +- +-#define TAC_PLUS_AUTHEN_FLAG_NOECHO 0x1 +- +- u_short msg_len; +- u_short data_len; +- +- /* */ +- /* */ +-}; +- +-#define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6 +- +-/* An authorization request packet */ +-struct author { +- u_char authen_method; +- u_char priv_lvl; +- u_char authen_type; +- u_char service; +- +- u_char user_len; +- u_char port_len; +- u_char rem_addr_len; +- u_char arg_cnt; /* the number of args */ +- +- /* */ +- /* */ +- /* */ +- /* */ +- /* */ +-}; +- +-#define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8 +- +-/* An authorization reply packet */ +-struct author_reply { +- u_char status; +- u_char arg_cnt; +- u_short msg_len; +- u_short data_len; +- +- /* */ +- /* */ +- /* */ +- /* */ +-}; +- +-#define TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE 6 +- +-struct acct { +- u_char flags; +- +-#define TAC_PLUS_ACCT_FLAG_MORE 0x1 +-#define TAC_PLUS_ACCT_FLAG_START 0x2 +-#define TAC_PLUS_ACCT_FLAG_STOP 0x4 +-#define TAC_PLUS_ACCT_FLAG_WATCHDOG 0x8 +- +- u_char authen_method; +- u_char priv_lvl; +- u_char authen_type; +- u_char authen_service; +- u_char user_len; +- u_char port_len; +- u_char rem_addr_len; +- u_char arg_cnt; /* the number of cmd args */ +- /* one u_char containing size for each arg */ +- /* */ +- /* */ +- /* */ +- /* char data for args 1 ... n */ +-}; +- +-#define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9 +- +-struct acct_reply { +- u_short msg_len; +- u_short data_len; +- u_char status; +- +-#define TAC_PLUS_ACCT_STATUS_SUCCESS 0x1 +-#define TAC_PLUS_ACCT_STATUS_ERROR 0x2 +-#define TAC_PLUS_ACCT_STATUS_FOLLOW 0x21 +- +-}; +- +-#define TAC_ACCT_REPLY_FIXED_FIELDS_SIZE 5 ++#define DOLLARSIGN '$' + + /* Odds and ends */ +-#define TAC_PLUS_MAX_ITERATIONS 50 +-#undef MIN +-#define MIN(a,b) ((a)<(b)?(a):(b)) + #define STREQ(a,b) (strcmp(a,b)==0) + #define MAX_INPUT_LINE_LEN 255 + +-/* 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_PROXY_FLAG 16384 +-#define DEBUG_MAXSESS_FLAG 32768 +-#define DEBUG_LOCK_FLAG 65536 +- +-extern char *codestring(); +-extern int keycode(); +- +-#define TAC_IS_USER 1 +-#define TAC_PLUS_RECURSE 1 +-#define TAC_PLUS_NORECURSE 0 +- +-#define DEFAULT_USERNAME "DEFAULT" +- +-#include "parse.h" +- +-/* 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 +- +-/* A parse tree node */ +-struct node { +- int type; /* node type (arg, svc, proto) */ +- void *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 */ +-}; +- +-typedef struct node NODE; +- +-union v { +- int intval; +- void *pval; +-}; +- +-typedef union v VALUE; +- +-/* acct.c */ +-extern void accounting(); +- +-/* report.c */ +-extern void report_string(); +-extern void report_hex(); +-#ifdef __STDC__ +-extern void report(int priority, char *fmt,...); +-#else +-extern void report(); +-#endif +- +-/* packet.c */ +-extern u_char *get_authen_continue(); +-extern int send_authen_reply(); +- +-/* utils.c */ +-extern char *tac_malloc(); +-extern char *tac_strdup(); +-extern char *tac_make_string(); +-extern char *tac_find_substring(); +-extern char *tac_realloc(); +- +-/* dump.c */ +-extern char *summarise_outgoing_packet_type(); +-extern char *summarise_incoming_packet_type(); +- +-/* author.c */ +-extern void author(); +- +-/* hash.c */ +-extern void *hash_add_entry(); +-extern void **hash_get_entries(); +-extern void *hash_lookup(); +- +-/* config.c */ +-extern int cfg_get_intvalue(); +-extern char * cfg_get_pvalue(); +-extern char *cfg_get_authen_default(); +-extern int cfg_get_authen_default_method(); +-extern char **cfg_get_svc_attrs(); +-extern NODE *cfg_get_cmd_node(); +-extern NODE *cfg_get_svc_node(); +-extern char *cfg_get_expires(); +-extern char *cfg_get_login_secret(); +-extern int cfg_get_user_nopasswd(); +-extern char *cfg_get_arap_secret(); +-extern char *cfg_get_chap_secret(); +-#ifdef MSCHAP +-extern char *cfg_get_mschap_secret(); +-#endif /* MSCHAP */ +-extern char *cfg_get_pap_secret(); +-extern char *cfg_get_opap_secret(); +-extern char *cfg_get_global_secret(); +-#ifdef USE_PAM +-extern char *cfg_get_pam_service(); +-#endif / *PAM */ +-extern void cfg_clean_config(); +-extern char *cfg_nodestring(); +- +-/* pw.c */ +-extern struct passwd *tac_passwd_lookup(); +- +-/* parse.c */ +-extern void parser_init(); +- +-/* pwlib.c */ +-extern void set_expiration_status(); +- + /* miscellaneous */ + #ifdef CONST_SYSERRLIST + extern const char *const sys_errlist[]; + #else + extern char *sys_errlist[]; + #endif +-extern int errno; +-extern int sendauth_fn(); +-extern int sendpass_fn(); +-extern int enable_fn(); +-extern int default_fn(); +-extern int default_v0_fn(); +-extern int skey_fn(); +-#ifdef MSCHAP +-extern void mschap_lmchallengeresponse(); +-extern void mschap_ntchallengeresponse(); +-#endif /* MSCHAP */ +- +-#ifdef MAXSESS +- +-extern void maxsess_loginit(); +-extern int maxsess_check_count(); +- +-/* +- * This is a shared file used to maintain a record of who's on +- */ +-#define WHOLOG_DEFAULT "/var/log/tac_who.log" +-extern char *wholog; +-/* +- * 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 */ +-}; +- +-#endif /* MAXSESS */ +- +-#ifdef USE_PAM +-extern int tac_pam_authorization(); +-#endif + +-#define LOGFILE_DEFAULT "/var/log/tac_plus.log" + +-extern struct timeval started_at; +-extern char *logfile; +-extern char *wtmpfile; +-extern int wtmpfd; ++#endif /* TAC_PLUS_H */ +diff --git a/tac_plus.pam b/tac_plus.pam +new file mode 100644 +index 0000000..fc135fb +--- /dev/null ++++ b/tac_plus.pam +@@ -0,0 +1,5 @@ ++#%PAM-1.0 ++auth required /lib/security/pam_pwdb.so shadow ++account required /lib/security/pam_pwdb.so ++password required /lib/security/pam_pwdb.so nullok use_authtok shadow ++session required /lib/security/pam_pwdb.so +diff --git a/tac_plus.spec.in b/tac_plus.spec.in +new file mode 100644 +index 0000000..6df6899 +--- /dev/null ++++ b/tac_plus.spec.in +@@ -0,0 +1,145 @@ ++# This is tac_plus rpm spec file ++ ++%define ver @VERSION@ ++%define rel 1 ++%define prefix /usr ++ ++Summary: Cisco Tacacs+ Daemon ++Name: tac_plus ++Version: %ver ++Release: %rel ++Copyright: Cisco systems, Inc. ++Group: Networking/Daemons ++Source: http://www.gazi.edu.tr/tacacs/src/tac_plus-%{ver}.tar.gz ++Url: http://www.gazi.edu.tr/tacacs/ ++Packager: Jan Kratochvil ++BuildRoot: /var/tmp/@PACKAGE@-%{ver}-%{rel}-root ++#Docdir: %{prefix}/doc ++ ++%define __libtoolize true # we don't need it, is is otherwise run automatically ++ # don't %undefine it, there is expansion bug at least in rpm-4.0-4 ++ ++%description ++TACACS+ daemon using with Cisco's NASs (Or other vendors) for AAA (Authentication , Authorization and Accounting) propose. ++ ++ ++%prep ++%setup ++ ++%build ++# configure script have some options describe below ++# --with-pam : For PAM support ++# --with-db : If you like to use db feature you must enable it ++# --with-mysql: For MySQL database support ++# --with-mysql-prefix: If you install MySQL libs other than /usr/lib ++# --enable-maxsess: For check concurrent logins (It's uses finger!!) ++# --with-pgsql With PgSQL Support ++# --with-pgsql-prefix=PREFIX PgSQL prefix [default=/usr] ++# --with-tacuid: If you like to run tac_plus specify UID ++# --with-tacgid: If you like to run tac_plus specify GID ++# --with-tacplus_pid=PREFIX Tac_plus pid file location [default=/var/run] ++# --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support ++ ++%configure --with-pam --with-db ++make ++ ++%install ++rm -rf "$RPM_BUILD_ROOT" ++%makeinstall ++install -d "$RPM_BUILD_ROOT"/%{_sysconfdir}/{tacacs,logrotate.d,pam.d,rc.d/{init.d,rc{0,1,2,3,4,5,6}.d}} ++install -c -m 0755 tac_plus.init "$RPM_BUILD_ROOT"/etc/rc.d/init.d/tac_plus ++install -b -c -m 0644 tac_plus.pam "$RPM_BUILD_ROOT"/etc/pam.d/pap ++install -b -c -m 0644 tac_plus.rotate "$RPM_BUILD_ROOT"/etc/logrotate.d/tac_plus ++ ++%clean ++rm -rf "$RPM_BUILD_ROOT" ++ ++%post ++/sbin/chkconfig --add tac_plus ++ ++%preun ++if [ $1 = 0 ]; then ++ if [ -f /var/lock/subsys/tac_plus ]; then ++ %{_sysconfdir}/rc.d/init.d/tac_plus stop ++ fi ++ /sbin/chkconfig --del tac_plus ++fi ++ ++%files ++%defattr(-, root, root) ++%config %{_sysconfdir}/tacacs/tac_plus.cfg ++%config %{_sysconfdir}/pam.d/pap ++%config %{_sysconfdir}/logrotate.d/tac_plus ++%doc users_guide CHANGES convert.pl ++%doc README.LDAP README.PAM tac_plus.sql ++%dir %{_sysconfdir}/tacacs ++%attr(750,root,root) %{_sysconfdir}/rc.d/init.d/tac_plus ++%attr(750,root,root) %{_bindir}/generate_passwd ++%attr(750,root,root) %{_sbindir}/tac_plus ++%attr(644,root,root) %{_mandir}/man1/* ++ ++%changelog ++* Mon Jul 9 2001 Jan Kratochvil ++- following changes supported by GTS (www.gts.com), cooperation by: ++ Pavel Ruzicka ++ Michael Macek ++- multiple "member" keyword memberships supported ++- "enlist" keyword supported to specify reverse memberships ++- "host" entity unified with "user"/"group" entities ++- "when" blocks implemented for NAS host based configuration ++- "authorization = recursive" implemented for full recursivity ++- line-trailing white spaces removed ++- function prototypes cleanup and K&R C compatibility ++- maintainer compilation is pedantic now, compiler warnings cleanup ++- uncomplete transition from system-name conditions to autoconf style ++- all Makefile options moved to configure.in ++- Makefile.in rewritten to automake Makefile.am ++- autogen script included for easy maintainer rebuilds ++- tac_plus.h split to headers for each particular source file ++- system regex is now preferred, own regex is just fallback ++- several files renamed to prevent auto*/system headers conflicts ++ ++* Sun Mar 25 2001 Devrim SERAL ++- Added PostgreSQL authentication and accounting function ++- Added tcpwrapper feature ++- Added LDAP Authentication from Harpes Patrick (patrick.harpes@tudor.lu) ++- Added more options to configure script ++- Added time_limit function for control user loging time ++- And more control for buffer overflow ++ ++* Fri Nov 17 2000 Devrim SERAL ++- packet.c is pached for overflow problem ++- Fix some log files name ++- Add new config parameters for database accounting ++- MySQL authentication code is functional ++- MySQL accounting code ready but not well tested ++ ++* Mon Mar 10 2000 Devrim SERAL ++- I am add PAM patch from Max Liccardo ++- Change PAM code to authorize user ++- Add db support from fil@artelecom.ru ++- I am write MySQL authentication code ++- MySQL code is still experimental ++ ++* Tue Nov 15 1999 Devrim SERAL ++- Take out documentation ++- Add more functional parameters tac_plus script ++- Change some code to authenticate with /etc/shadow ++- Fix some file permissions (Like accounting logs file) ++ ++* Sun Oct 24 1999 D'mon ++- I moved to RedHat 6.0 =) ++- changes of the package internals! ++ ++* Mon Oct 18 1999 D'mon ++- massive remake to suit RedHat 5.2 standard ++- patch for RedHat 5.2 ++ ++* Wed Aug 4 1999 Erhan Bilgili ++- fixes for the RPM_OPT_FLAGS ++- change the buildroot to /var/tmp/tacacsd ++ ++* Wed Aug 4 1999 Devrim SERAL ++- I just re-did the spec file ++- And added Tacac FAQ ++ +diff --git a/tac_regexp.c b/tac_regexp.c +index 9c0357a..390c5f8 100644 +--- a/tac_regexp.c ++++ b/tac_regexp.c +@@ -42,9 +42,23 @@ + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ ++ ++ ++#include "tac_plus.h" ++ ++#ifdef WITH_INCLUDED_REGEX ++ + #include +-#include "regexp.h" +-#include "regmagic.h" ++#include ++#include /* malloc() can be found in OR */ ++#ifdef HAVE_MALLOC_H ++#include ++#endif ++ ++#include "tac_regexp.h" ++#include "tac_regmagic.h" ++#include "report.h" /* for regerror() */ ++ + + /* + * The "internal use only" fields in regexp.h are present to pass info from +@@ -149,7 +163,7 @@ + #define UCHARAT(p) ((int)*(p)&CHARBITS) + #endif + +-#define FAIL(m) { regerror(m); return(NULL); } ++#define FAIL(m) { tac_regerror(m); return(NULL); } + #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') + #define META "^$.[()|?+*\\" + +@@ -164,7 +178,7 @@ + /* + * Global work variables for regcomp(). + */ +-static char *regparse; /* Input-scan pointer. */ ++static const char *regparse; /* Input-scan pointer. */ + static int regnpar; /* () count. */ + static char regdummy; + static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +@@ -173,23 +187,24 @@ static long regsize; /* Code size. */ + /* + * Forward declarations for regcomp()'s friends. + */ +-#ifndef STATIC +-#define STATIC static +-#endif +-STATIC char *reg(); +-STATIC char *regbranch(); +-STATIC char *regpiece(); +-STATIC char *regatom(); +-STATIC char *regnode(); +-STATIC char *regnext(); +-STATIC void regc(); +-STATIC void reginsert(); +-STATIC void regtail(); +-STATIC void regoptail(); +-#ifdef STRCSPN +-STATIC int strcspn(); ++static char *reg TAC_ARGS((int paren, int *flagp)); ++static char *regbranch TAC_ARGS((int *flagp)); ++static char *regpiece TAC_ARGS((int *flagp)); ++static char *regatom TAC_ARGS((int *flagp)); ++static char *regnode TAC_ARGS((int op)); ++static void regc TAC_ARGS((int b)); ++static void reginsert TAC_ARGS((int op, char *opnd)); ++static void regtail TAC_ARGS((char *p, char *val)); ++static void regoptail TAC_ARGS((char *p, char *val)); ++static int regtry TAC_ARGS((tac_regexp *prog, const char *string)); ++static int regmatch TAC_ARGS((char *prog)); ++static int regrepeat TAC_ARGS((char *p)); ++static char *regnext TAC_ARGS((register char *p)); ++#ifndef HAVE_STRCSPN ++static int strcspn TAC_ARGS((char *s1, char *s2)); + #endif + ++ + /* + - regcomp - compile a regular expression into internal code + * +@@ -205,16 +220,18 @@ STATIC int strcspn(); + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +-regexp * +-regcomp(exp) +-char *exp; ++ ++tac_regexp *tac_regcomp TAC_ARGS((const char *exp)); ++ ++tac_regexp * ++tac_regcomp(exp) ++const char *exp; + { +- register regexp *r; ++ register tac_regexp *r; + register char *scan; + register char *longest; + register int len; + int flags; +- extern char *malloc(); + + if (exp == NULL) + FAIL("NULL argument"); +@@ -233,7 +250,7 @@ char *exp; + FAIL("regexp too big"); + + /* Allocate space. */ +- r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); ++ r = (tac_regexp *)malloc(sizeof(tac_regexp) + (unsigned)regsize); + if (r == NULL) + FAIL("out of space"); + +@@ -272,7 +289,7 @@ char *exp; + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) +- if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= (unsigned)len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } +@@ -293,6 +310,9 @@ char *exp; + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ ++ ++static char *reg TAC_ARGS((int paren, int *flagp)); ++ + static char * + reg(paren, flagp) + int paren; /* Parenthesized? */ +@@ -301,7 +321,7 @@ int *flagp; + register char *ret; + register char *br; + register char *ender; +- register int parno; ++ register int parno = 0 /* GCC paranoia */; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ +@@ -365,6 +385,9 @@ int *flagp; + * + * Implements the concatenation operator. + */ ++ ++static char *regbranch TAC_ARGS((int *flagp)); ++ + static char * + regbranch(flagp) + int *flagp; +@@ -404,6 +427,9 @@ int *flagp; + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ ++ ++static char *regpiece TAC_ARGS((int *flagp)); ++ + static char * + regpiece(flagp) + int *flagp; +@@ -468,6 +494,9 @@ int *flagp; + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ ++ ++static char *regatom TAC_ARGS((int *flagp)); ++ + static char * + regatom(flagp) + int *flagp; +@@ -577,9 +606,12 @@ int *flagp; + /* + - regnode - emit a node + */ ++ ++static char *regnode TAC_ARGS((int op)); ++ + static char * /* Location. */ + regnode(op) +-char op; ++int op; /* promoted "char" type */ + { + register char *ret; + register char *ptr; +@@ -602,9 +634,12 @@ char op; + /* + - regc - emit (if appropriate) a byte of code + */ ++ ++static void regc TAC_ARGS((int b)); ++ + static void + regc(b) +-char b; ++int b; /* promoted "char" type */ + { + if (regcode != ®dummy) + *regcode++ = b; +@@ -617,9 +652,12 @@ char b; + * + * Means relocating the operand. + */ ++ ++static void reginsert TAC_ARGS((int op, char *opnd)); ++ + static void + reginsert(op, opnd) +-char op; ++int op; /* promoted "char" type */ + char *opnd; + { + register char *src; +@@ -646,6 +684,9 @@ char *opnd; + /* + - regtail - set the next-pointer at the end of a node chain + */ ++ ++static void regtail TAC_ARGS((char *p, char *val)); ++ + static void + regtail(p, val) + char *p; +@@ -678,6 +719,9 @@ char *val; + /* + - regoptail - regtail on operand of first argument; nop if operandless + */ ++ ++static void regoptail TAC_ARGS((char *p, char *val)); ++ + static void + regoptail(p, val) + char *p; +@@ -696,44 +740,40 @@ char *val; + /* + * Global work variables for regexec(). + */ +-static char *reginput; /* String-input pointer. */ +-static char *regbol; /* Beginning of input, for ^ check. */ +-static char **regstartp; /* Pointer to startp array. */ +-static char **regendp; /* Ditto for endp. */ ++static const char *reginput; /* String-input pointer. */ ++static const char *regbol; /* Beginning of input, for ^ check. */ ++static const char **regstartp; /* Pointer to startp array. */ ++static const char **regendp; /* Ditto for endp. */ + +-/* +- * Forwards. +- */ +-STATIC int regtry(); +-STATIC int regmatch(); +-STATIC int regrepeat(); + + #ifdef DEBUG + int regnarrate = 0; +-void regdump(); +-STATIC char *regprop(); ++static char *regprop TAC_ARGS((char *op)); ++static void regdump TAC_ARGS((tac_regexp *r)); + #endif + + /* + - regexec - match a regexp against a string + */ ++ ++int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string)); ++ + int +-regexec(prog, string) +-register regexp *prog; +-register char *string; ++tac_regexec(prog, string) ++register tac_regexp *prog; ++register const char *string; + { +- register char *s; +- extern char *strchr(); ++ register const char *s; + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { +- regerror("NULL parameter"); ++ tac_regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { +- regerror("corrupted program"); ++ tac_regerror("corrupted program"); + return(0); + } + +@@ -779,14 +819,17 @@ register char *string; + /* + - regtry - try match at specific point + */ ++ ++static int regtry TAC_ARGS((tac_regexp *prog, const char *string)); ++ + static int /* 0 failure, 1 success */ + regtry(prog, string) +-regexp *prog; +-char *string; ++tac_regexp *prog; ++const char *string; + { + register int i; +- register char **sp; +- register char **ep; ++ register const char **sp; ++ register const char **ep; + + reginput = string; + regstartp = prog->startp; +@@ -816,13 +859,15 @@ char *string; + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ ++ ++static int regmatch TAC_ARGS((char *prog)); ++ + static int /* 0 failure, 1 success */ + regmatch(prog) + char *prog; + { + register char *scan; /* Current node. */ + char *next; /* Next node. */ +- extern char *strchr(); + + scan = prog; + #ifdef DEBUG +@@ -888,7 +933,7 @@ char *prog; + case OPEN+8: + case OPEN+9: { + register int no; +- register char *save; ++ register const char *save; + + no = OP(scan) - OPEN; + save = reginput; +@@ -916,7 +961,7 @@ char *prog; + case CLOSE+8: + case CLOSE+9: { + register int no; +- register char *save; ++ register const char *save; + + no = OP(scan) - CLOSE; + save = reginput; +@@ -935,7 +980,7 @@ char *prog; + } + break; + case BRANCH: { +- register char *save; ++ register const char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ +@@ -956,7 +1001,7 @@ char *prog; + case PLUS: { + register char nextch; + register int no; +- register char *save; ++ register const char *save; + register int min; + + /* +@@ -985,7 +1030,7 @@ char *prog; + return(1); /* Success! */ + break; + default: +- regerror("memory corruption"); ++ tac_regerror("memory corruption"); + return(0); + break; + } +@@ -997,21 +1042,23 @@ char *prog; + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ +- regerror("corrupted pointers"); ++ tac_regerror("corrupted pointers"); + return(0); + } + + /* + - regrepeat - repeatedly match something simple, report how many + */ ++ ++static int regrepeat TAC_ARGS((char *p)); ++ + static int + regrepeat(p) + char *p; + { + register int count = 0; +- register char *scan; ++ register const char *scan; + register char *opnd; +- extern char *strchr(); + + scan = reginput; + opnd = OPERAND(p); +@@ -1039,7 +1086,7 @@ char *p; + } + break; + default: /* Oh dear. Called inappropriately. */ +- regerror("internal foulup"); ++ tac_regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } +@@ -1051,6 +1098,9 @@ char *p; + /* + - regnext - dig the "next" pointer out of a node + */ ++ ++static char *regnext TAC_ARGS((register char *p)); ++ + static char * + regnext(p) + register char *p; +@@ -1072,19 +1122,19 @@ register char *p; + + #ifdef DEBUG + +-STATIC char *regprop(); +- + /* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +-void ++ ++static void regdump TAC_ARGS((tac_regexp *r)); ++ ++static void + regdump(r) +-regexp *r; ++tac_regexp *r; + { + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *next; +- extern char *strchr(); + + + s = r->program + 1; +@@ -1121,6 +1171,9 @@ regexp *r; + /* + - regprop - printable representation of opcode + */ ++ ++static char *regprop TAC_ARGS((char *op)); ++ + static char * + regprop(op) + char *op; +@@ -1192,7 +1245,7 @@ char *op; + p = "PLUS"; + break; + default: +- regerror("corrupted opcode"); ++ tac_regerror("corrupted opcode"); + break; + } + if (p != NULL) +@@ -1207,12 +1260,14 @@ char *op; + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +-#ifdef STRCSPN ++#ifndef HAVE_STRCSPN + /* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + ++static int strcspn TAC_ARGS((char *s1, char *s2)); ++ + static int + strcspn(s1, s2) + char *s1; +@@ -1231,4 +1286,10 @@ char *s2; + } + return(count); + } +-#endif ++#endif /* HAVE_STRCSPN */ ++ ++#else /* WITH_INCLUDED_REGEX */ ++ ++TAC_SOURCEFILE_EMPTY ++ ++#endif /* WITH_INCLUDED_REGEX */ +diff --git a/tac_regexp.h b/tac_regexp.h +index b23d97e..7a1a884 100644 +--- a/tac_regexp.h ++++ b/tac_regexp.h +@@ -1,3 +1,10 @@ ++#ifndef TAC_REGEXP_H ++#define TAC_REGEXP_H 1 ++ ++#include "tac_plus.h" ++ ++#ifdef WITH_INCLUDED_REGEX ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -17,6 +24,7 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + /* + * Definitions etc. for regexp(3) routines. + * +@@ -24,17 +32,21 @@ + * not the System V one. + */ + #define NSUBEXP 10 +-typedef struct regexp { +- char *startp[NSUBEXP]; +- char *endp[NSUBEXP]; ++typedef struct tac_regexp { ++ const char *startp[NSUBEXP]; ++ const char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +-} regexp; ++} tac_regexp; ++ ++ ++extern tac_regexp *tac_regcomp TAC_ARGS((const char *exp)); ++extern int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string)); ++ ++ ++#endif /* WITH_INCLUDED_REGEX */ + +-extern regexp *regcomp(); +-extern int regexec(); +-extern void regsub(); +-extern void regerror(); ++#endif /* TAC_REGEXP_H */ +diff --git a/tac_regmagic.h b/tac_regmagic.h +index 0b18b0a..ff321df 100644 +--- a/tac_regmagic.h ++++ b/tac_regmagic.h +@@ -1,3 +1,8 @@ ++#ifndef REGMAGIC_H ++#define REGMAGIC_H 1 ++ ++#include "tac_plus.h" ++ + /* + Copyright (c) 1995-1998 by Cisco systems, Inc. + +@@ -17,8 +22,12 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + ++ + /* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ + #define MAGIC 0234 ++ ++ ++#endif /* REGMAGIC_H */ +diff --git a/tcpwrap.c b/tcpwrap.c +index ef3d3ad..744a1df 100644 +--- a/tcpwrap.c ++++ b/tcpwrap.c +@@ -4,21 +4,41 @@ + Writen by Devrim SERAL. This file protected by + GNU Copyright agreement. + */ ++ ++ ++#include "tac_plus.h" ++ + #ifdef TCPWRAPPER ++ + #include +-#include "tac_plus.h" + +-int allow_severity = LOG_INFO; ++#include "tcpwrap.h" ++#include "report.h" ++#include "packet.h" ++#include "do_author.h" /* for "struct identity" */ ++#include "main.h" ++ ++ ++int allow_severity = LOG_INFO; /* *_severity accessed from libwrap */ + int deny_severity = LOG_WARNING; + + ++/* Configurable: ++ */ ++ ++/* Define tac_plus name for hosts.* files */ ++#define TACNAME "tac_plus" ++ ++ ++int check_from_wrap TAC_ARGS((struct identity *datap)); ++ + int + check_from_wrap(datap) + struct identity *datap; + { + struct request_info req; + +- request_init(&req, RQ_DAEMON,TACNAME,RQ_CLIENT_ADDR,datap->NAS_name , NULL); ++ request_init(&req, RQ_FILE,session.sock,RQ_DAEMON,TACNAME,RQ_CLIENT_ADDR,datap->NAS_name , NULL); + fromhost(&req); /* validate client host info */ + if (!hosts_access(&req)) + { +@@ -34,4 +54,9 @@ struct identity *datap; + return 1; + + } ++ ++#else /* TCPWRAPPER */ ++ ++TAC_SOURCEFILE_EMPTY ++ + #endif /* TCPWRAPPER */ +diff --git a/tcpwrap.h b/tcpwrap.h +new file mode 100644 +index 0000000..e8e9455 +--- /dev/null ++++ b/tcpwrap.h +@@ -0,0 +1,16 @@ ++#ifndef TCPWRAP_H ++#define TCPWRAP_H 1 ++ ++#include "tac_plus.h" ++ ++#ifdef TCPWRAPPER ++ ++ ++struct identity; ++ ++extern int check_from_wrap TAC_ARGS((struct identity *datap)); ++ ++ ++#endif /* TCPWRAPPER */ ++ ++#endif /* TCPWRAP_H */ +diff --git a/time_limit.c b/time_limit.c +index 08f6c55..75062f2 100644 +--- a/time_limit.c ++++ b/time_limit.c +@@ -25,180 +25,224 @@ Software Foundation; either version 2, or (at your option) any later version. + + */ + +-#include"time_limit.h" ++ + #include "tac_plus.h" + +-int problem=0; ++#include ++#include ++#include ++#include ++ ++#include "time_limit.h" ++#include "report.h" ++#include "main.h" ++#include "utils.h" ++ ++ ++static int str_token_proc TAC_ARGS((char *str)); ++static int process TAC_ARGS((char *str)); ++static int time_calc TAC_ARGS((char *str, int lct)); ++static int antoi TAC_ARGS((char *str, int n)); ++ ++ ++static char* week_days[] = {"SU","MO","TU","WE","TH","FR","SA","WK","WD","AL"}; ++static long week_day_val[] = {1, 2, 4, 8, 16, 32, 64, 62, 65, 127}; ++ ++static int problem = 0; ++ ++ ++int time_limit_process TAC_ARGS((const char *str)); + + int + time_limit_process(str) +-char *str; ++const char *str; + { +-int ret=0; +-char *tmp_str; ++ int ret=0; ++ char *tmp_str, *str_copy; + +-tmp_str=(char *)strtok(str,",|"); +-while ( tmp_str != NULL) { +- ret|=str_token_proc(tmp_str); +- tmp_str=(char *)strtok(NULL,","); ++ str_copy = tac_strdup(str); ++ tmp_str = (char *) strtok(str_copy,",|"); ++ ++ while ( tmp_str != NULL) { ++ ret |= str_token_proc(tmp_str); ++ tmp_str = (char *) strtok(NULL,","); + } +-return (ret); ++ free(str_copy); ++ ++ return (ret); + } + +-int ++static int str_token_proc TAC_ARGS((char *str)); ++ ++static int + str_token_proc(str) + char *str; + { +-int inv=0,ret; ++ int inv = 0, ret; + +-/* Pass space characters */ +-while (isspace(*str)) str++; ++ /* Pass space characters */ ++ while (isspace((int) *str)) ++ str++; + +-if (*str=='!') { +- inv=1;str++; +-} ++ if (*str=='!') { ++ inv=1; ++ str++; ++ } + +-ret=process(str); ++ ret=process(str); + +-if (problem) { ++ if (problem) { + if ( debug & DEBUG_AUTHEN_FLAG ) + report(LOG_DEBUG,"Timestamp format incorrect"); + problem=0; + return(0); +-} ++ } + +-if (inv) ++ if (inv) + ret=!ret; +-return(ret); ++ ++ return(ret); + } + + +-int ++static void str_up TAC_ARGS((char *str)); ++ ++static void ++str_up(str) ++char *str; ++{ ++ while (*str) { ++ if (islower((int) *str)) ++ *str = toupper((int) *str); ++ str++; ++ } ++} ++ ++ ++static int process TAC_ARGS((char *str)); ++ ++static int + process(str) + char *str; + { +-int count=0,ret=0,i,j,localtm; +-char *head,*buf,*gec; +-long sec; +-struct tm *tms; ++ int count = 0, ret = 0, i, j, localtm; ++ char *head, *buf, *gec; ++ time_t sec; ++ struct tm *tms; + +-/* Pass space characters */ +-while (isspace(*str)) str++; ++ /* Pass space characters */ ++ while (isspace((int) *str)) ++ str++; + +-head=str; ++ head=str; + +-/* Count alphanumeric char */ +-while (isalpha(*str)) { ++ /* Count alphanumeric char */ ++ while (isalpha((int) *str)) { + count++; + str++; +-} ++ } + +-if ( count==0 || count%2 ) { ++ if ( count==0 || count%2 ) { + problem++; + return 0; +-} ++ } + +-buf=(char *)malloc(count+1); +-strncpy(buf,head,count); +-gec=buf; +-str_up(buf); ++ buf = (char *) tac_malloc(count+1); ++ strncpy(buf, head, count); ++ gec = buf; ++ str_up(buf); + +-for(i=1;i<=(count/2);i++) { +- for (j=0;jtm_hour)*60+tms->tm_min; +-ret=( week_day_val[tms->tm_wday] & ret ) && time_calc(str,localtm); ++ sec = time(NULL); ++ tms = localtime(&sec); ++ localtm = (tms->tm_hour)*60 + tms->tm_min; ++ ret = ( week_day_val[tms->tm_wday] & ret ) && time_calc(str,localtm); + +-if (ret>0) ++ if (ret>0) + return (1); +-else +- return(0); ++ else ++ return (0); + } + +-str_up(str) +-char *str; +-{ +- while(*str) { +- if(islower(*str)) *str=toupper(*str); +- str++; +- } +-} + +-int +-time_calc(str,lct) ++static int time_calc TAC_ARGS((char *str, int lct)); ++ ++static int ++time_calc(str, lct) + char *str; + int lct; + { +-char *t1,*t2,*head; +-int say1,say2,count=0; ++ char *t1, *t2, *head; ++ int say1, say2, count=0; + +-head=str; ++ head = str; + +- while (isdigit(*head) || *head=='-') { ++ while (isdigit((int) *head) || *head=='-') { + count++; + head++; + } + +-if (*str=='\0' || count!= TPL ) { ++ if ( *str=='\0' || count!= TPL ) { + problem++; + return (0); +-} ++ } + +- t1=(char *) malloc(count); +- strncpy(t1,str,count); /*Put str value to t1*/ ++ t1 = (char *) tac_malloc(count); ++ strncpy(t1, str, count); /* Put str value to t1 */ + +- t2=(char *) strstr(t1,"-"); /* Find next time part */ ++ t2 = (char *) strstr(t1,"-"); /* Find next time part */ + +-if (t2==NULL) { ++ if (t2==NULL) { + free(t1); + problem++; + return(0); +-} ++ } + +-*t2='\0';t2++; ++ *t2++ = '\0'; + +-if ( strlen(t1)<4 || strlen(t2)<4 ) { ++ if ( strlen(t1)<4 || strlen(t2)<4 ) { + free(t1); + problem++; + return(0); +-} +- say1=antoi(t1,2)*60+antoi(t1+2,2); +- say2=antoi(t2,2)*60+antoi(t2+2,2); ++ } ++ say1 = antoi(t1,2)*60 + antoi(t1+2,2); ++ say2 = antoi(t2,2)*60 + antoi(t2+2,2); + +-free(t1); ++ free(t1); + +-if (say1<=say2) { +- if( (lct>=say1) && (lct<=say2) ) return(1); +-} +-else { +- if( (lct>=say1) || (lct<=say2) ) return(1); ++ if (say1 <= say2) { ++ if( (lct>=say1) && (lct<=say2) ) ++ return(1); ++ } else { ++ if( (lct>=say1) || (lct<=say2) ) ++ return(1); ++ } ++ return(0); + } +-return(0); + +-} ++static int antoi TAC_ARGS((char *str, int n)); + +-int +-antoi(str,n) +-char *str;int n; ++static int ++antoi(str, n) ++char *str; ++int n; + { +-char *buf; +-int ret; ++ char *buf; ++ int ret; + +- buf=(char *) malloc(n); +- strncpy(buf,str,n); +- ret=atoi(buf); ++ buf = (char *) tac_malloc(n); ++ strncpy(buf, str, n); ++ ret = atoi(buf); + free(buf); + +-return(ret); ++ return(ret); + } +diff --git a/time_limit.h b/time_limit.h +index e8bb6fb..eac2096 100644 +--- a/time_limit.h ++++ b/time_limit.h +@@ -1,13 +1,14 @@ +-#include +-#include +-#include +-#include +-#include ++#ifndef TIME_LIMIT_H ++#define TIME_LIMIT_H 1 ++ ++#include "tac_plus.h" ++ ++ + #define NUM 10 + #define TPL 9 /* time part len */ + +-/*Global variables */ +-static char* week_days[]={"SU","MO","TU","WE","TH","FR","SA","WK","WD","AL"}; +-static long week_day_val[]={1,2,4,8,16,32,64,62,65,127}; + +-extern int time_limit_process(); ++extern int time_limit_process TAC_ARGS((const char *str)); ++ ++ ++#endif /* TIME_LIMIT_H */ +diff --git a/users_guide b/users_guide +index 2626115..c1a8cfd 100644 +--- a/users_guide ++++ b/users_guide +@@ -337,6 +337,83 @@ and specify as many groupwide characteristics in the group declaration + as possible. Then, individual user declarations can be used to + override the group settings for selected users as needed. + ++MEMBERSHIP IN MULTIPLE GROUPS ++----------------------------- ++ ++Besides the descibed single-membership of user to some group, you may also find ++useful if user (or host) belongs to multiple groups at once. You can naturally ++specify multiple "member = group_X" commands for such user: ++ ++user = fred { ++ # fred is a member of both groups: admins_company_A, admins_company_B ++ member = admins_company_A ++ member = admins_company_B ++} ++ ++group = admins_company_A { ++ # group admins_company_A is not a member of any group ++ member = admins_company_A_privilege_X ++ member = admins_company_A_privilege_Y ++} ++group = admins_company_A_privilege_X { ++} ++group = admins_company_A_privilege_Y { ++} ++ ++group = admins_company_B { ++ # group admins_company_B is not a member of any group ++} ++ ++Here it is important to respect the ordering of "member" commands: Any ++searching for attributes/values is done by Depth-First Search - so Daemon would ++first try to look all members of admins_company_A and THEN (after it would ++failed to find any) it would start to searching through admins_company_B. The ++searching through the proposed example would be done in the following order: ++ ++ fred ++ admins_company_A ++ admins_company_A_privilege_X ++ admins_company_A_privilege_Y ++ admins_company_B ++ ++Sometimes you would want to only list some members (users, hosts or groups) but ++you don't want to specify any attributes for them. You would be able to do it: ++ ++group = city_X_NASes { ++} ++host first.NAS.X.city { ++ member = city_X_NASes ++} ++host second.NAS.X.city { ++ member = city_X_NASes ++} ++ ++But you will probably find more comfortable to use "enlist" keyword. It has ++the same functionality but it goes from the other way: ++ ++ member: current entity is connected as CHILD to the specified PARENT entity ++ enlist: specified entity is connected as CHILD to the current one as PARENT ++ ++The example would be re-written using "enlist" keyword as: ++ ++group = city_X_NASes { ++ enlist = host first.NAS.X.city ++ enlist = host second.NAS.X.city ++} ++ ++As you can see, "enlist" doesn't require the existence of the given entity as ++it would loose its primary purpose. If the entity doesn't exist it will be ++automatically created (as empty one) - this doesn't apply to "member"! Any ++argument to "member" MUST already exist. "enlist" is provided to save you from ++writing a lot of empty definition lines like: ++ ++host = first.NAS.X.city { ++} ++ ++All forward references are not a problem, you can still make membership or ++enlistment on the top of the file with the entity which will be defined on the ++end of the configuration file. ++ + CONFIGURING USER AUTHENTICATION + ------------------------------- + +@@ -1013,6 +1090,229 @@ group = admin { + } + + ++CONFIGURATION RESPECTING NAS HOST OF THE USER ++--------------------------------------------- ++ ++Sometimes you would want to modify the configuration file according to the ++source NAS where the user is being authenticated/authorized. For example if you ++are big ISP you want to permit administrator of company X to be able to monitor ++status of the links on NAS in company X but, of course, she should be able to ++monitor any other links on any other NAS of the same ISP. As all the NASes are ++authorized from the same Daemon, it seems as a problem. (You can workaround it ++by using custom authorization program - see "USING PROGRAMS TO DO ++AUTHORIZATION" section below - but it is not nice solution.) ++ ++For this purposes there exists another entity 'host': ++ ++ user wilma ++ group admin ++ host 198.133.219.25 ++ host nas.cisco.com ++ ++As you can see you may use either IP address or DNS name. Anyway, we strongly ++recommend to always use only IP addresses - DNS subsystem may fail or it may be ++forged by the enemy. ++ ++You have two methods of utilizing the differences between NASes: ++ ++1) Current user is always automatically enlisted (=given membership to) to its ++ current NAS host. This looks weird as only groups can have members but this ++ is the only exception to this rule, current NAS host can really have a ++ member: ++ ++ ( user "current_user" | user DEFAULT ) ++ | ++ +-- host "current_NAS_IP_address" ++ | | ++ | +- group DEFAULT ++ | ++ +-- host "current_NAS_hostname" ++ | | ++ | +-- group DEFAULT ++ | ++ +-- group DEFAULT ++ ++ Each link only exists in the case it there exist both its peers, of course. ++ user/group DEFAULT is written here only for the completeness of the chart. ++ DEFAULT is discussed elsewhere in this documentation. ++ ++ According to the shown ordering, attributes/values in the host of current ++ NAS identified by its IP address has _higher_ precence over the attributes ++ in the current NAS identified by its (reverse-resolved) hostname. ++ ++ According to this auto-connections, you can for example permit some command ++ to ALL the users on such NAS: ++ ++user = fred { ++ login = cleartext LLLL ++} ++host = machine.A.company { ++ cmd = write { ++ permit terminal ++ } ++} ++ ++ In this configuration file ALL the valid users can do "write terminal" when ++ logged in on NAS "machine.A.company". ++ ++2) Sometimes you need to do the authorization specific only to some users on ++ some NASes. For example to permit "write terminal" ONLY to user fred ++ connected to NAS "machine.A.company". That means that you want to forbid it ++ to user fred on any other NAS and yuo also want to forbid it to all the ++ other users on NAS "machine.A.company". (Line "authorization = recursive" is ++ required but read the following section "FULL RECURSIVITY" to know all its ++ consequences.) ++ ++authorization = recursive ++user = fred { ++ login = cleartext LLLL ++} ++host = machine.A.company { ++ when = user fred { ++ cmd = write { ++ permit terminal ++ } ++ } ++} ++ ++ This file has the same effect as: ++ ++authorization = recursive ++user = fred { ++ login = cleartext LLLL ++ when = host machine.A.company { ++ cmd = write { ++ permit terminal ++ } ++ } ++} ++ ++ You can see the (nested) command "when" can limit the scope of existence of ++ its contents. Definition of "host machine.A.company" with empty block (no ++ attributes) isn't needed as "when" line will automatically create hosts not ++ defined elsewhere, in the same style as "enlist" keyword creates them. Any ++ "user"s or "group"s referenced by "when" MUST be defined, such entities are ++ never created automatically! ++ ++ Unfortunately you cannot use "when" to limit any items, just a few of them ++ are possible: ++ ++member ++enlist ++cmd ++cmd arguments (to limit specific "permit"/"deny" lines) ++service (or incl. "protocol" specification) ++service AV pairs (to limit specific "attr=value" lines) ++when (enabling pure nesting of "when" blocks) ++ ++ Full flexibility to limit any contents may be done in future (needs complete ++ cfgfile.c rewrite) but currently it is not supported. Fortunately you can ++ get the same behaviour by appropriate usage of "member" keyword - all the ++ attributes to be conditioned are put into separate group and you limit only ++ the "member" keyword to such group. ++ ++ "when" command has the form "when = CONDITION { BLOCK }", CONDITION can be: ++ ++user USR i.e. that current user is "USR") ++host HOSTNAME i.e. that current user is on NAS "HOSTNAME") ++group GRP i.e. current user belongs to the group "GRP", ++ All possible "when" conditions to reach such belonging ++ have to be successfuly met to make this condition successful. ++ Realize that "member" can be limited by "when" keyword. ++CONDITION and CONDITION and CONDITION ... ++CONDITION or CONDITION or CONDITION ... ++not CONDITION ++( CONDITION ) ++ ++ You can see that you CANNOT use for example "user A and user B or user C", ++ such condition would have ambiguous precedence of operators, you must ++ explicitly write: ( user A and user B ) or user C ++ or: user A and ( user B or user C ) ++ ++ Both parentheses have to be written as separate words, NOT "(user A)" ++ but "( user A )" instead. ++ (Proper solution would also need the complete cfgfile.c rewrite.) ++ ++ "not" operator has the highest precendence, so: not user A or user B ++ has the same meaning as: ( not user A ) or user B ++ ++ Sometimes the "when" condition is so-called unresolvable. Example: ++ ++group = GRP { } ++user = USR { ++ when = group GRP { ++ member = GRP ++ } ++} ++ ++ It is looping, when we would be the member of GRP, we would be really the ++ member of GRP but otherwise sould wouldn't be the member of GRP. Weird? ++ Yes, such case is considered as configuration bug, it is reported as: ++ ++ Unable to resolve expression from line 243, some looping occured ++ ++ Generally such unresolvable conditional expression is considered as UNKNOWN ++ and the "when" keyword is then evaluated as "false" (=skip it's block). ++ This MAY produce unwanted privilege access (if you were conditionally ++ forbidding some privileges) so always watch out all the error messages in ++ logs! (You should generally do default deny and specifically only "permit" ++ all the privileges so the unintentional unresolvable expressions shouldn't ++ hurt you in real.) ++ ++FULL RECURSIVITY ++---------------- ++ ++We have written in the previous examples line: ++ ++authorization = recursive ++ ++This changes some behaviour of Daemon: ++ ++1) Looping of memberships: By default any looped memberships are reported as ++ error with message: recursively defined groups: ... ++ ++ After "authorization = recursive" any looping is silently accepted as it ++ would be sometimes hard to prevent it in some complex configurations. ++ Searching is done in normal Depth-First Search, but traversion is ++ backtracked one step when the entity was already visited. Simply it will ++ work 'magically', just safely ignore the fact. ++ ++2) "default service = default" is supported only with: ++ authorization = recursive ++ This isn't any real change, just some formality. In fact each entity block ++ has "default service = default" in effect as default (in the case you don't ++ write any "default service =" assign). ++ ++ "authorization = recursive" is enforced here due to the change of Daemon ++ behaviour: In non-recursive (old) case the Daemon assumes "deny .*" as the ++ last line of "cmd" block. This makes proper NAS-host based permissions ++ impossible. In "authorization = recursive" mode it requires appropriate ++ "permit" or "deny" line to apply - otherwise the Depth-First Search through ++ the entities will continue to find another applicable "cmd" block (toplevel ++ "default authorization" may get into effect as the last resort). ++ ++ "default service" keyword takes then another meaning: In non-recursive mode ++ it will mean "if the command wasn't found". In fully-recursive mode it will ++ mean "if any cmd block didn't decide". ++ ++ Please keep in mind that "service =" (with its possible "protocol =" mate) ++ has always the same behaviour, nothing is changed with "authorization = ++ recursive" enabled. Its functionality is already a bit complex (with AV ++ pairs get erased, which added, which copied, which are optional from NAS, ++ which optional from Daemon, whether to do default permit or deny etc.). ++ Fortunately we didn't found that there would be needed some extended ++ 'recursiveness' functionality of "service" for application of NAS-host based ++ authorization. ++ ++3) "host" entities are not recursive at all (=its attributes aren't looked up ++ in its parent groups) without "authorization = recursive". This isn't any ++ much change as in the previous versions of Daemon "host" entity was either ++ completely unsupported or it was supported only with the only one attribute: ++ ++ "key": Attribute defines different protocol key (instead of the specified ++ toplevel one) for the specified host. Its use is recommended to ++ improve overall network security (by using unique key for each NAS). ++ + USING PROGRAMS TO DO AUTHORIZATION + ---------------------------------- + +diff --git a/utils.c b/utils.c +index b597e70..43b50f3 100644 +--- a/utils.c ++++ b/utils.c +@@ -17,19 +17,36 @@ + FITNESS FOR A PARTICULAR PURPOSE. + */ + +-#include "tac_plus.h" + +-#ifdef STDLIB_MALLOC ++#include "tac_plus.h" + +-#include ++#include /* malloc() can be found in OR */ ++#ifdef HAVE_MALLOC_H ++#include ++#endif ++#include ++#ifdef HAVE_FCNTL_H ++#include /* for "struct flock" */ ++#endif ++#include ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++#ifdef HAVE_SYSLOG_H ++#include ++#endif ++#ifdef HAVE_SYS_SYSLOG_H ++#include ++#endif + +-#else /* !STDLIB_MALLOC */ ++#include "utils.h" ++#include "report.h" ++#include "main.h" + +-#include + +-#endif /* STDLIB_MALLOC */ ++void *tac_malloc TAC_ARGS((int size)); + +-char * ++void * + tac_malloc(size) + int size; + { +@@ -49,9 +66,11 @@ int size; + return (p); + } + +-char * ++void *tac_realloc TAC_ARGS((void *ptr, int size)); ++ ++void * + tac_realloc(ptr, size) +-char *ptr; ++void *ptr; + int size; + { + char *p; +@@ -70,6 +89,9 @@ int size; + return (p); + } + ++void tac_exit TAC_ARGS((int status)) G_GNUC_NORETURN; ++ ++void + tac_exit(status) + int status; + { +@@ -78,9 +100,11 @@ int status; + exit(status); + } + ++char *tac_strdup TAC_ARGS((const char *p)); ++ + char * + tac_strdup(p) +-char *p; ++const char *p; + { + char *n = strdup(p); + +@@ -91,6 +115,8 @@ char *p; + return (n); + } + ++char *tac_make_string TAC_ARGS((u_char *p, int len)); ++ + char * + tac_make_string(p, len) + u_char *p; +@@ -116,9 +142,13 @@ int len; + /* return a pointer to the end of substring in string, or NULL. Substring + must begin at start of string. + */ +-char * ++ ++const char *tac_find_substring TAC_ARGS((const char *substring, const char *string)); ++ ++const char * + tac_find_substring(substring, string) +-char *substring, *string; ++const char *substring; ++const char *string; + { + int len; + +@@ -190,6 +220,8 @@ bcmp(s1,s2,n) + are at the mercy of SUN's lockd, which is probably a bad idea + */ + ++int tac_lockfd TAC_ARGS((char *filename, int lockfd)); ++ + int + tac_lockfd (filename, lockfd) + char *filename; +@@ -273,7 +305,8 @@ int lockfd; + are at the mercy of SUN's lockd, which is probably a bad idea + */ + +-int ++#if 0 /* unused */ ++static int + tac_unlockfd (filename,lockfd) + char *filename; + int lockfd; +@@ -303,3 +336,184 @@ int lockfd; + } + return(0); + } ++#endif /* unused */ ++ ++/* Management of bidirectional lists. ++*/ ++ ++#ifdef TAC_LIST_PARANOIA ++ ++static void tac_list_check_magic TAC_ARGS((const struct tac_list *list)); ++ ++static void ++tac_list_check_magic(list) ++const struct tac_list *list; ++{ ++ if (list->_magic != TAC_LIST_MAGIC) ++ report(LOG_ERR, "MAGIC fail for tac_list"); ++} ++ ++static void tac_list_node_check_magic TAC_ARGS((const struct tac_list_node *node)); ++ ++static void ++tac_list_node_check_magic(node) ++const struct tac_list_node *node; ++{ ++ if (node->_magic != TAC_LIST_NODE_MAGIC) ++ report(LOG_ERR, "MAGIC fail for tac_list_node"); ++} ++#else /* TAC_LIST_PARANOIA */ ++ ++#define tac_list_check_magic(list) ++#define tac_list_node_check_magic(node) ++ ++#endif /* TAC_LIST_PARANOIA */ ++ ++ ++void tac_list_init TAC_ARGS((struct tac_list *list)); ++ ++void ++tac_list_init(list) ++struct tac_list *list; ++{ ++#ifdef TAC_LIST_PARANOIA ++ list->_magic = TAC_LIST_MAGIC; ++#endif ++ list->_head = NULL; ++ list->_tail = NULL; ++ ++ tac_list_check_magic(list); ++} ++ ++void tac_list_node_init TAC_ARGS((struct tac_list_node *node)); ++ ++void ++tac_list_node_init(node) ++struct tac_list_node *node; ++{ ++#ifdef TAC_LIST_PARANOIA ++ node->_magic = TAC_LIST_NODE_MAGIC; ++#endif ++ node->_list = NULL; ++ ++ tac_list_node_check_magic(node); ++} ++ ++struct tac_list *tac_list_node_get_list TAC_ARGS((struct tac_list_node *node)); ++ ++struct tac_list * ++tac_list_node_get_list(node) ++struct tac_list_node *node; ++{ ++ tac_list_node_check_magic(node); ++ ++ return (node->_list); ++} ++ ++struct tac_list_node *tac_list_first_node TAC_ARGS((struct tac_list *list)); ++ ++struct tac_list_node * ++tac_list_first_node(list) ++struct tac_list *list; ++{ ++ tac_list_check_magic(list); ++ ++ return (list->_head); ++} ++ ++#if 0 /* unused */ ++struct tac_list_node *tac_list_last_node TAC_ARGS((struct tac_list *list)); ++ ++struct tac_list_node * ++tac_list_last_node(list) ++struct tac_list *list; ++{ ++ tac_list_check_magic(list); ++ ++ return (list->_tail); ++} ++#endif /* unused */ ++ ++void tac_list_addhead TAC_ARGS((struct tac_list *list, struct tac_list_node *node)); ++ ++void ++tac_list_addhead(list, node) ++struct tac_list *list; ++struct tac_list_node *node; ++{ ++ tac_list_check_magic(list); ++ tac_list_node_check_magic(node); ++ ++ if ((node->_next = list->_head)) ++ node->_next->_prev = node; ++ list->_head = node; ++ node->_prev = NULL; ++ if (!list->_tail) ++ list->_tail = node; ++ node->_list = list; ++} ++ ++void tac_list_addtail TAC_ARGS((struct tac_list *list, struct tac_list_node *node)); ++ ++void ++tac_list_addtail(list, node) ++struct tac_list *list; ++struct tac_list_node *node; ++{ ++ tac_list_check_magic(list); ++ tac_list_node_check_magic(node); ++ ++ if ((node->_prev = list->_tail)) ++ node->_prev->_next = node; ++ list->_tail = node; ++ node->_next = NULL; ++ if (!list->_head) ++ list->_head = node; ++ node->_list = list; ++} ++ ++struct tac_list_node *tac_list_node_next TAC_ARGS((struct tac_list_node *node)); ++ ++struct tac_list_node * ++tac_list_node_next(node) ++struct tac_list_node *node; ++{ ++ tac_list_node_check_magic(node); ++ ++ return (node->_next); ++} ++ ++#if 0 /* unused */ ++struct tac_list_node *tac_list_node_prev TAC_ARGS((struct tac_list_node *node)); ++ ++struct tac_list_node * ++tac_list_node_prev(node) ++struct tac_list_node *node; ++{ ++ tac_list_node_check_magic(node); ++ ++ return (node->_prev); ++} ++#endif /* unused */ ++ ++void tac_list_node_remove TAC_ARGS((struct tac_list_node *node)); ++ ++void ++tac_list_node_remove(node) ++struct tac_list_node *node; ++{ ++ tac_list_node_check_magic(node); ++ tac_list_check_magic(node->_list); ++ ++ if (node->_next) ++ node->_next->_prev = node->_prev; ++ else ++ node->_list->_tail = node->_prev; ++ ++ if (node->_prev) ++ node->_prev->_next = node->_next; ++ else ++ node->_list->_head = node->_next; ++ ++ node->_list = NULL; ++} +diff --git a/utils.h b/utils.h +new file mode 100644 +index 0000000..f8c39e8 +--- /dev/null ++++ b/utils.h +@@ -0,0 +1,59 @@ ++#ifndef UTILS_H ++#define UTILS_H 1 ++ ++#include "tac_plus.h" ++ ++#include /* for u_* */ ++ ++ ++/* Configurable: ++ */ ++#define TAC_LIST_PARANOIA 1 ++ ++ ++/* Bidirectional lists */ ++ ++#ifdef TAC_LIST_PARANOIA ++#define TAC_LIST_MAGIC (0xBEF78147U) ++#define TAC_LIST_NODE_MAGIC (0x297DA735U) ++#endif ++ ++struct tac_list { ++#ifdef TAC_LIST_PARANOIA ++ unsigned _magic; ++#endif ++ struct tac_list_node *_head, *_tail; ++}; ++ ++struct tac_list_node { ++#ifdef TAC_LIST_PARANOIA ++ unsigned _magic; ++#endif ++ struct tac_list_node *_next, *_prev; ++ struct tac_list *_list; ++}; ++ ++ ++extern void *tac_malloc TAC_ARGS((int size)); ++extern void *tac_realloc TAC_ARGS((void *ptr, int size)); ++extern void tac_exit TAC_ARGS((int status)) G_GNUC_NORETURN; ++extern char *tac_strdup TAC_ARGS((const char *p)); ++extern char *tac_make_string TAC_ARGS((u_char *p, int len)); ++extern const char *tac_find_substring TAC_ARGS((const char *substring, const char *string)); ++extern int tac_lockfd TAC_ARGS((char *filename, int lockfd)); ++ ++/* Bidirectional lists: */ ++ ++extern void tac_list_init TAC_ARGS((struct tac_list *list)); ++extern void tac_list_node_init TAC_ARGS((struct tac_list_node *node)); ++extern struct tac_list *tac_list_node_get_list TAC_ARGS((struct tac_list_node *node)); ++struct tac_list_node *tac_list_first_node TAC_ARGS((struct tac_list *list)); ++struct tac_list_node *tac_list_last_node TAC_ARGS((struct tac_list *list)); ++extern void tac_list_addhead TAC_ARGS((struct tac_list *list, struct tac_list_node *node)); ++extern void tac_list_addtail TAC_ARGS((struct tac_list *list, struct tac_list_node *node)); ++extern struct tac_list_node *tac_list_node_next TAC_ARGS((struct tac_list_node *node)); ++extern struct tac_list_node *tac_list_node_prev TAC_ARGS((struct tac_list_node *node)); ++extern void tac_list_node_remove TAC_ARGS((struct tac_list_node *node)); ++ ++ ++#endif /* UTILS_H */ diff --git a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz b/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz deleted file mode 100644 index a6b722c..0000000 Binary files a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz and /dev/null differ diff --git a/project/tcpoverudp/Index.pm b/project/tcpoverudp/Index.pm index c84c811..37e63ab 100644 --- a/project/tcpoverudp/Index.pm +++ b/project/tcpoverudp/Index.pm @@ -33,7 +33,7 @@ our @ListItem=( "platform"=>"unixuser", "priority"=>555, "cvs"=>"tcpoverudp", - "link-Source file"=>'http://git.jankratochvil.net/?p=tcpoverudp.git;a=blob_plain;hb=HEAD;f=tcpoverudp', + "link-Source file"=>'//git.jankratochvil.net/?p=tcpoverudp.git;a=blob_plain;hb=HEAD;f=tcpoverudp', "summary"=>"Forward TCP connections over UDP without root", "license"=>"GPL", "maintenance"=>"ready", diff --git a/project/xbelnormalize/Index.pm b/project/xbelnormalize/Index.pm index a23f293..e633957 100644 --- a/project/xbelnormalize/Index.pm +++ b/project/xbelnormalize/Index.pm @@ -32,7 +32,7 @@ our @ListItem=( "name"=>"xbelnormalize", "platform"=>"unixuser", "priority"=>585, - "link-Source file"=>'http://git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/xbelnormalize', + "link-Source file"=>'//git.jankratochvil.net/?p=nethome.git;a=blob_plain;hb=HEAD;f=bin/xbelnormalize', "summary"=>"Bookmarks .xbel file normalizator", "license"=>"GPL", "maintenance"=>"ready", diff --git a/resume/DeveloperConference2011-debug.pdf b/resume/DeveloperConference2011-debug.pdf new file mode 100644 index 0000000..752ab1d Binary files /dev/null and b/resume/DeveloperConference2011-debug.pdf differ diff --git a/resume/ResumeJanKratochvil.pdf b/resume/ResumeJanKratochvil.pdf index e43ef8f..60aacf1 100644 Binary files a/resume/ResumeJanKratochvil.pdf and b/resume/ResumeJanKratochvil.pdf differ diff --git a/resume/ResumeJanKratochvil.pm b/resume/ResumeJanKratochvil.pm index b064583..b2feb15 100644 --- a/resume/ResumeJanKratochvil.pm +++ b/resume/ResumeJanKratochvil.pm @@ -34,18 +34,17 @@ sub Contact($) my($self)=@_; return ( - "Name" =>"Jan Kratochvil", - "English" =>"technical: fluent, general: communicable", - "Born" =>"1979", - "Sex" =>"male", - "eMail" =>a_href('mailto:web@jankratochvil.net'), - "Projects"=>a_href('http://www.jankratochvil.net/project/'), - "Resume" =>a_href('http://www.jankratochvil.net/resume/')."
    " + "name" =>"Jan Kratochvil", + "English" =>"fluent", + "born" =>"1979", + "e-mail" =>a_href('mailto:web@jankratochvil.net'), + "projects before 2005" + =>a_href('http://www.jankratochvil.net/project/'), + "resume" =>a_href('http://www.jankratochvil.net/resume/')."
    " ."[ ".join(" | ", a_href('/resume/ResumeJanKratochvil.pdf','PDF'), - a_href('/resume/ResumeJanKratochvil.txt','text'), )." ]", - "OpenPGP" =>a_href('/pgp-JanKratochvil.txt','8A48C6AC'), + "OpenPGP" =>a_href('/pgp-JanKratochvil.txt','1E3AD15E'), ); } @@ -68,85 +67,6 @@ $W->{"args"}{"Wabs"}=1; My::Web->heading(); -my %item=( - project::Lib->name_to_hashref(), - product::Lib->name_to_hashref(undef(),"override"=>{"platform"=>"product"}), - ); -my @itemnames=sort { - ($item{$b}{"priority"} <=> $item{$a}{"priority"}) - or (lc($item{$a}->{"name"}) cmp lc($item{$b}->{"name"})); - } keys(%item); - -my $projectref=sub -{ -my($name,%args)=@_; - - my $project_product=($args{"product"} ? "product" : "project"); - return a_href "/$project_product/$name/",$item{$name}{"name"},"attr"=>'class="'.$project_product.'"'; -}; - -# $args{"parenthesis"}=1; -# $args{"product"}=1; -my $project=sub -{ -my($name,%args)=@_; - - my $r=&{$projectref}($name,%args); - if (!$args{"bare"}) { - my $summary=$item{$name}{"summary"}; - $summary=~s#(?:]*>|)##gi; # if $args{"unhref"}; - if (!$args{"parenthesis"}) { - $r.=": $summary"; - } - else { - $r.=" ($summary)"; - } - } - return $r; -}; - -my $free_projects=sub (@) -{ -my(@platforms)=@_; - - my $r=""; - $r.=join ", ",map({ my $platform=$_; - map({ (0 - # || $item{$_}{"sponsorship"} - || $item{$_}{"trivia"} - || $item{$_}{"platform"} ne $platform ? () : (&{$projectref}($_))); } @itemnames); - } @platforms); - return $r; -}; - - -my $reference=sub ($$) -{ -my($mail,$fullname)=@_; - - my $r=""; - $r.=''; - $r.=$fullname; - if (lc($W->{"args"}{"referees"}) eq "referees") { - # &Crypt::Rot13::rot13 - $mail=~tr/a-zA-Z/n-za-mN-ZA-M/; - $r.=' <'.a_href("mailto:$mail").'>'; - } - $r.=''; - return $r; -}; - -sub techs ($) -{ -my($arg)=@_; - - return '('.$arg.')'; -} - - -my $uClinux=a_href 'http://www.uclinux.com/','uClinux'; -my $now=(localtime())[5]+1900; - print <<"HERE"; HERE @@ -166,206 +86,8 @@ print <<"HERE"; @{[ vskip "2ex" ]} -@{[ #

    Not looking for a new job.

    -]} - -@{[ vskip "2ex" ]} - -
    - - - - - - - - - -
    Fulltime Jobs
    DateCompanyDescription
    2006/07-...@{[ a_href 'http://www.redhat.com/','Red Hat' ]} - @{[ a_href 'https://en.wikipedia.org/wiki/LLDB_(debugger)','LLDB' ]} development, @{[ a_href 'https://en.wikipedia.org/wiki/Executable_and_Linkable_Format','ELF' ]}, @{[ a_href 'https://en.wikipedia.org/wiki/DWARF','DWARF' ]}, @{[ a_href 'https://en.wikipedia.org/wiki/ptrace','ptrace' ]}, @{[ a_href 'https://en.wikipedia.org/wiki/Call_stack#Unwinding','unwinding' ]}, @{[ a_href 'https://en.wikipedia.org/wiki/Application_binary_interface','ABI' ]}, formerly @{[ a_href 'https://en.wikipedia.org/wiki/GNU_Debugger','GDB' ]} development -
    2005/04-11@{[ - a_href_cc {""=>'http://www.valinux.co.jp/en/', - "JP"=>'http://www.valinux.co.jp/'},'VA Linux Systems Japan' ]}@{[ a_href 'http://www.kernel.org/','Linux kernel' ]} - based software development
    2004/01-11@{[ a_href 'http://www.sun.com/','Sun Microsystems' ]}@{[ a_href 'http://www.sun.com/software/javaenterprisesystem/','Java Enterprise System' ]} - Quality Assurance (JES QA), technical lead
    - JES components compatibility analysis, automation of QA tasks, - JES testing, QA frameworks pilot deployments, - architecture and lead of the team projects, team programming guidelines -
    - -@{[ vskip "1ex" ]} - - - - - - - - - - - -
    Freelance Commercial Projects
    DateDescription
    2005-2006@{[ &$project('mms2',"product"=>1) ]} (@{[ a_href_cc {""=>'http://www.mms2.org/', - "CZ"=>'http://www.mms2.cz/', - "SK"=>'http://www.mms2.cz/'},'service web' ]})
    - Web interface was outsourced. - Commercial service management held together with company JK-Invent. -
      -
    • Designed to coexist with existing GSM networks service servers
    • -
    • MMSE/WAP-Push/web/mail convergency featuring smart transformations
    • -
    • Credits charging by ISDN IVR + automatic bank transaction acceptance interface
    • -
    • Completed alpha and beta product cycle for mobile phones firmware bugs workarounds
    • -
    • Free gatewaying program for customers to avoid targetted GSM operators firewalling
    • -
    -
    2004 @{[ &$project('hotelgate',"product"=>1) ]}
    - Plug&Play TCP/IP for clients, selectively ticketed and/or free access, - simplified interface for reception desks, easy and unified management.
    - European facility installations provided by @{[ a_href 'http://www.jklabs.cz/','JK Labs' ]}. -
    2002-2003@{[ &$project('captive') ]}
    - Featured @{[ a_href 'http://slashdot.org/article.pl?sid=03/12/02/1536227','on slashdot' ]}.
    - Analysis of portions of 80MB+ i386 disassembly dumps of MS-Windows XP kernel and ntfs.sys - for the first binary compatible reimplementation of the undocumented XP kernel subsystems.
    - Written @{[ &$project('TraceFS') ]}: MS-Windows Kernel API Tracer - as native W32 kernel hooking driver for run-time analysis of the NT Cache Manager - XP kernel subsystem.
    - UNIX userland of Captive provides GnomeVFS interface to the MS-Windows kernel - based MS-Windows filesystem isolated by the CORBA/ORBit interface. -
    - -@{[ vskip "1ex" ]} - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Contractor Jobs
    DateClientDescription
    2001-2002@{[ a_href('http://www.atspraha.cz/','Advanced Telecom Services') ]}Mobile technologies -
      -
    • MMS framework client via EuroTel GSM operator - by technologies GSM MMS, Perl, XML, SOAP
      - Project included - @{[ a_href "/etmms/",'pilot MMS Center debugging',"attr"=>'class="project"' ]} with - Ethereal (@{[ a_href 'http://www.ethereal.com','external link' ]}), fixes of Ethereal - (@{[ a_href '/project/etherealmmse/','1' ]}, @{[ a_href '/project/etherealwsp/','2' ]}). -
    • -
    • Multimedia EMS support implemented by @{[ &$project('gsmperl',"bare"=>1) ]}
    • -
    • Implementation of protocol BIP (based on Nokia CIMD2 protocol) for GSM SMS Center direct communication - by extending gnokii (@{[ a_href 'http://www.gnokii.org/','external link' ]}) - for EuroTel GSM operator
    • -
    -
    1999-2000@{[ a_href('http://www.suse.com/','SuSE') ]}@{[ &$project('surprise') ]}
    - Project was completed as a team work. The code is based on Gnome technologies. - As one of the subtasks to keep system bootability I implemented @{[ &$project('int13sniff',"bare"=>1) ]} - to ease Microsoft boot loaders disassembly across partition modifications. -
    2001-2002@{[ a_href('http://www.readynote.com/','ReadyNote') ]}Mobile technologies for embedded devices -
      -
    • Completed debugging of gnokii (@{[ a_href 'http://www.gnokii.org/','external link' ]}) GSM software modem - for Nokia 5190
    • -
    • Port of gnokii and my @{[ &$project('mdsms',"parenthesis"=>1) ]} to embedded - uClinux (@{[ a_href 'http://www.uclinux.org/','external link' ]})
    • -
    -
    1999-2003@{[ a_href('http://www.jklabs.cz/','JKLabs') ]}System administration of GNU/Linux and FreeBSD, techsupport: GuestNET, - @{[ a_href 'http://www.2m.dk/web/html_version/eclipse/eclipse.html','Eclipse' ]}, - @{[ a_href 'http://www.globaloop.com/','GlobaLoop' ]}, - @{[ a_href 'http://www.antlimited.com/products/fresco.htm','Fresco' ]}
    - Ad hoc technical support challenges such as i386 IRQ redirector, - @{[ &$project('middleman',"parenthesis"=>1,"unhref"=>1) ]} or @{[ &$project('ssht',"parenthesis"=>1,"unhref"=>1) ]}. -
    2001 @{[ a_href('http://www.gtsgroup.cz/','GTS') ]}@{[ &$project('tac_plus') ]}
    1999 @{[ a_href('http://www.unicom-prague.cz/','Unicom') ]}Technical translations from English
    1998-1999@{[ a_href('http://www.geoinvest.cz/','Geoinvest') ]}Mobile technologies, embedded devices -
      -
    • @{[ &$project('332') ]}
    • -
    • Siemens M1 GSM modem daemon for GPS, client/server setup, remote GSM terminals
    • -
    -
    1999 @{[ a_href('http://www.tencom.cz/','TENcom Trade') ]}Mobile technologies -
      -
    • @{[ a_href "/project/mdsms/","MobilDock SMS Tool","attr"=>'class="project"' ]}: - SMS communication software for Digital UNIX
    • -
    -
    1998 @{[ a_href('http://www.elsa-online.org/',"European Law Students' Association") ]}@{[ &$project('step') ]} incl. fixes of - @{[ a_href "/project/phphash/","PHP","attr"=>'class="project"' ]} and - @{[ a_href "/project/pgsqlsubstr/","PostgreSQL","attr"=>'class="project"' ]}
    1993-1996Profes J&KPC assembly and customer service
    1993 @{[ a_href('http://www.japhila.cz/index_en.htm','Japhila') ]}StampMan: Database application @{[ techs 'MS-DOS: Pascal, plainTeX' ]}
    - -@{[ vskip "2ex" ]} - - - - - - - - -
    Awards
    1997Bronze medal in the International Olympiad in Informatics, South Africa - Cape Town
    19972nd in an MO-P national contest - mathematics Olympiad, the programming category
    19971st in the International Competition in Programming
    19964th in the International Competition in Programming
    19957th in the Central European Olympiad in Informatics
    - -@{[ vskip "2ex" ]} - - - - - - - - - - - - - - - - -
    Experience Summary
    Major Areas mobile technologies, filesystems, embedded devices
    Platforms GNU/Linux/i386/UNIX/FreeBSD, W32 kernel, Amiga/680x0, MS-DOS, - ZX Spectrum, handheld PC-E500S
    Languages C (Gnome/GCC), Perl, bash/awk/sed..., - Java, C++, Pascal, Basic, REXX, S-Lang, Foxplus, Prolog
    Tools/Metalanguagesautoconf, automake, libtool, m4, CVS, gettext, bison, flex, ld script, - gdb script, Maple V
    Assemblers i386/x86_64, Motorola 680x0, Zilog Z80, Intel 8051, MIPS R2/3000, - (Hitachi SH-8)
    WWW-Related HTML/XHTML, CSS, PHP, SQL (PostgreSQL, MySQL), JavaScript, CGI
    DocumentationplainTeX, LaTeX, DocBook, DocBook Lite, XML/NS/Schema, - gtk-doc, nroff, pod
    Libraries Gnome/GTK+/GLib, POSIX/BSD/SysV/threads/sockets, X11, OpenGL, NCurses, - S-Lang, GnomeVFS, libxml, libneon, SVGALib, NIS, Qt,...
    RPC CORBA/ORBit, SOAP, WSDL, Sun RPC
    Protocols GSM SMS/PDU/Nokia Smart Messaging/EMS/WAP/WTP/WSP/MMSE/SMIL/WML/AMR/ETSI stds/3GPP stds, - IPv4, HTTP, SMTP/RFC822/MIME, FTP, SSH, DHCP, POP3, IMAP4, NFS v2/v3, PPP/LCP/IPCP,...
    Filesystems ext2, FAT, AmigaFFS, ISO-9660
    Certificates @{[ a_href('https://www.redhat.com/rhtapps/certification/verify/?certId=110-518-099','RHCE 110-518-099') ]}
    +

    @{[ a_href('/resume/ResumeJanKratochvil.pdf','My PDF resume.') ]}

    -@{[ vskip "2ex" ]} - - - - - - - - -HERE - my @platforms=@project::Lib::platforms; - while (@platforms) { - my $platform_sym =shift @platforms; - my $platform_name=shift @platforms; - print ""; - print ''; - print ''; - print "\n"; - } -print <<"HERE"; - -
    My Own Free Projects
    - Detailed listing available at: @{[ a_href 'http://www.jankratochvil.net/projects/' ]} -
    PlatformAvailable Free Projects
    '.a_href('/project/#'.$platform_sym,$platform_name).''.&{$free_projects}($platform_sym).'
    HERE diff --git a/resume/ResumeJanKratochvil.tex b/resume/ResumeJanKratochvil.tex index 0b1a9dc..ff77e86 100644 --- a/resume/ResumeJanKratochvil.tex +++ b/resume/ResumeJanKratochvil.tex @@ -1,10 +1,10 @@ -% $Id$ - - -\documentclass{article} +\documentclass[a4paper]{article} \usepackage[dvipdf]{graphicx} \usepackage{multirow} \usepackage[colorlinks]{hyperref} +\usepackage{tabls} + +\setlength\extrarulesep{1pt} \setlength\belowcaptionskip{2pt} \newlength{\intertableskip} @@ -26,17 +26,6 @@ \fi \vskip\belowcaptionskip} -% Discard vskips of itemizers. -\def\itemizel{\itemize -\setlength\itemsep\z@ -\setlength\@topsep\z@ -\setlength\@topsepadd\z@ -\setlength\parskip\z@ -} -\def\enditemizel{\enditemize -\hrule height\z@ -} - \makeatother \pdfinfo{ @@ -58,17 +47,16 @@ \begin{table}[!h] \centering - \begin{tabular*}{0.49\textwidth}{|@{\extracolsep{\fill}}c@{\extracolsep{\fill}}l@{\extracolsep{\fill}}l@{\extracolsep{\fill}}|} + \begin{tabular*}{0.57\textwidth}{|@{\extracolsep{\fill}}c@{\extracolsep{\fill}}l@{\extracolsep{\fill}}l@{\extracolsep{\fill}}|} \hline \ % - {} & Name & Jan Kratochvil \\ - {} & English & technical: fluent, general: communicable\ \ \ \\ - {} & Born & 1979 \\ - {} & Sex & male \\ - {} & eMail & \href{mailto:job@jankratochvil.net}{job@jankratochvil.net} \\ - {} & Projects & \href{http://www.jankratochvil.net/project/}{http://www.jankratochvil.net/project/} \\ - {} & Resume & \href{http://www.jankratochvil.net/resume/}{http://www.jankratochvil.net/resume/} \\ - {} & OpenPGP & \href{http://www.jankratochvil.net/pgp-JanKratochvil.txt}{8A48C6AC} \\ + {} & name & Jan Kratochvil \\ + {} & English & fluent\ \ \ \\ + {} & born & 1979 \\ + {} & e-mail & \href{mailto:job@jankratochvil.net}{job@jankratochvil.net} \\ + {} & projects before 2005\ \ & \href{https://www.jankratochvil.net/project/}{https://www.jankratochvil.net/project/}\ \ \\ + {} & resume & \href{https://www.jankratochvil.net/resume/}{https://www.jankratochvil.net/resume/} \\ + {} & OpenPGP & \href{https://www.jankratochvil.net/pgp-JanKratochvil.txt}{1E3AD15E} \\ \hline \end{tabular*} \end{table} @@ -81,85 +69,78 @@ Date & Company & Description \\ \hline -2006/07-\dots & \href{http://www.redhat.com/}{Red Hat} & - \href{https://en.wikipedia.org/wiki/LLDB_(debugger)}{LLDB} development, \href{https://en.wikipedia.org/wiki/Executable_and_Linkable_Format}{ELF}, \href{https://en.wikipedia.org/wiki/DWARF}{DWARF}, \href{https://en.wikipedia.org/wiki/ptrace}{ptrace}, \href{https://en.wikipedia.org/wiki/Call_stack#Unwinding}{unwinding}, \href{https://en.wikipedia.org/wiki/Application_binary_interface}{ABI}, formerly \href{https://en.wikipedia.org/wiki/GNU_Debugger}{GDB} development +2022-\dots & \href{https://www.azul.com/}{Azyl Systems} & + Development of OpenJDK in C++/C/assembler. \\ \hline -2005/04-11 & \href{http://www.valinux.co.jp/}{VA Linux Systems\hfil\newline Japan} & - \href{http://www.kernel.org/}{Linux kernel} based development +2022 & \href{https://www.barclays.co.uk/}{Barclays} & + Software development in C++. \\ \hline -2004/01-11 & \href{http://www.sun.com/}{Sun Microsystems} & - \href{http://www.sun.com/software/javaenterprisesystem/}{Java Enterprise System} - Quality Assurance (JES~QA), technical lead\hfill\newline - JES components compatibility analysis, automation of QA~tasks, - JES testing, QA~frameworks pilot deployments, - architecture and lead of the team projects, team programming guidelines - \\ -\hline +2006-2021 & \href{https://www.redhat.com/}{Red Hat} & + \href{https://en.wikipedia.org/wiki/LLDB_(debugger)}{LLDB} debugger developer and + formerly \href{https://en.wikipedia.org/wiki/GNU_Debugger}{GDB} debugger developer. - \end{tabular*} -\end{table} + \href{https://en.wikipedia.org/wiki/GNU_Debugger}{GDB} developed primarily 2007-2013, 1231 commits: + \href{https://github.com/bminor/binutils-gdb/commits?author=jankratochvil}{log}. -\vskip\intertableskip + I was GDB maintainer for \href{https://en.wikipedia.org/wiki/Fedora_(operating_system)}{Fedora}\&\href{https://en.wikipedia.org/wiki/Red_Hat_Enterprise_Linux}{RHEL}. + Larger features I have implemented: \href{https://en.wikipedia.org/wiki/Position-independent_executable}{PIE} support, + DWARF-5 entry values \& tail calls, Fortran modules, DWARF-5 excl. DWO incl. + .debug\_names index producer \& consumer excl. namespaces. Implemented in + Fedora but never upstreamed: C\&Fortran \href{https://en.wikipedia.org/wiki/Variable-length_array}{VLA}, build-id. -\begin{table}[!h] - \centering - \caption{Freelance Commercial Projects} - \begin{tabular*}{\textwidth}{|l@{\extracolsep{\fill}}p{5.7in}|} - \hline -Date & Description \\ -\hline + \href{https://en.wikipedia.org/wiki/LLDB_(debugger)}{LLDB} developed 2017-2021, 159 commits: + \href{https://github.com/llvm/llvm-project/commits?author=jankratochvil}{log}, + \href{https://reviews.llvm.org/p/jankratochvil/}{reviews}. -\hypertarget{mms2}{2005-2006} & \href{http://www.jankratochvil.net/product/mms2/}{MMS2}: Alternative center for cheaper mobile phones multimedia messages (MMS) + Developed \href{https://sourceware.org/systemtap/wiki/utrace/tests}{Linux kernel ptrace testsuite} (most of the testcases are mine). - Web interface was outsourced. - Commercial service management held together with JK-Invent. + My presentations about \href{https://en.wikipedia.org/wiki/GNU_Debugger}{GDB}: + \href{https://www.jankratochvil.net/resume/DeveloperConference2011-debug.pdf}{2011} + \href{https://www.jankratochvil.net/resume/gdb2012.pdf}{2012} + \href{https://www.jankratochvil.net/resume/devconf2017.pdf}{2017} - \begin{itemizel} + My presentation about \href{https://en.wikipedia.org/wiki/LLDB_(debugger)}{LLDB}: + \href{https://www.jankratochvil.net/resume/devconf2019.pdf}{2019} - \item - Designed to coexist with existing GSM networks service servers + My article about \href{https://en.wikipedia.org/wiki/LLVM}{LLVM}/\href{https://en.wikipedia.org/wiki/AddressSanitizer}{Sanitizers}: + \href{https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind#}{2021} - \item - MMSE/WAP-Push/web/mail convergency featuring smart transformations + My \href{https://en.wikipedia.org/wiki/DWARF}{DWARF} standard + clarifications: + \href{http://www.dwarfstd.org/ShowIssue.php?issue=100416.1}{1} + \href{http://www.dwarfstd.org/ShowIssue.php?issue=090321.1}{2} + \href{http://www.dwarfstd.org/ShowIssue.php?issue=161215.3}{3} + \href{http://www.dwarfstd.org/ShowIssue.php?issue=161113.1}{4} + \href{http://www.dwarfstd.org/ShowIssue.php?issue=110926.1}{5} + \href{http://www.dwarfstd.org/ShowIssue.php?issue=100923.1}{6}, + extensions: + \href{http://www.dwarfstd.org/ShowIssue.php?issue=161102.1}{1}, + \href{http://www.dwarfstd.org/ShowIssue.php?issue=170527.1}{2}, + fix: + \href{http://www.dwarfstd.org/ShowIssue.php?issue=161027.1}{1}. - \item - Credits charging by ISDN IVR + automatic bank transaction acceptance + My \href{https://lab.llvm.org/staging/#/builders/16}{LLDB buildbot}, its \href{https://github.com/llvm/llvm-zorg/commits?author=jankratochvil}{implementation}. - \item - Completed mobile phones firmware bugs analysis and workarounds + Completed \href{https://en.wikipedia.org/wiki/Red_Hat_Certified_Engineer}{RHCE} certificate \href{https://rhtapps.redhat.com/verify?certId=110-518-099}{110-518-099}. - \item - Free gatewaying program to avoid targetted GSM operators firewalling - \end{itemizel} + Wrote many bugfixes and bugreports for various components of \href{https://en.wikipedia.org/wiki/Fedora_(operating_system)}{Fedora} + \href{https://en.wikipedia.org/wiki/Free_software}{Free} operating system. \\ \hline -2004 & \href{http://www.jankratochvil.net/product/hotelgate/}{HotelGate}: Internet Public Access Gateway - - Plug\&Play TCP/IP for clients, selectively ticketed and/or free access, - simplified interface for reception desks, easy management. - European facility installations provided by \href{http://www.jklabs.cz/}{JK Labs}. +2005 & \href{https://www.valinux.co.jp/}{VA Linux Systems\hfil\newline Japan} & + \href{https://www.kernel.org/}{Linux kernel} -- first ever Linux kernel relocatability + for~\href{https://en.wikipedia.org/wiki/Kdump_(Linux)}{kdump}-alike,\linebreak + \href{https://en.wikipedia.org/wiki/Network_File_System}{NFS (Network File System)} server caching \\ \hline -\hypertarget{captive}{2002-2003} & \href{http://www.jankratochvil.net/project/captive/}{Captive}: The first free NTFS read/write filesystem for GNU/Linux - - Featured \href{http://slashdot.org/article.pl?sid=03/12/02/1536227}{on slashdot}. - - Analysis of portions of 80MB+ i386 disassembly dumps of - MS-Windows XP kernel and ntfs.sys for the first binary compatible - reimplementation of the undocumented XP kernel subsystems. - - Written \href{http://www.jankratochvil.net/project/TraceFS/}{TraceFS}: - MS-Windows Kernel API Tracer as native W32 kernel hooking driver for - run-time analysis of the NT Cache Manager XP kernel subsystem. - - UNIX userland of Captive provides GnomeVFS interface to the MS-Windows kernel - based MS-Windows filesystem isolated by the CORBA/ORBit interface. +2004 & \href{https://en.wikipedia.org/wiki/Sun_Microsystems}{Sun Microsystems} & + \href{https://en.wikipedia.org/wiki/Sun_Java_System}{Sun Java Enterprise System} Quality Assurance (JES~QA), technical lead \\ \hline @@ -168,129 +149,108 @@ Date & Description \\ \vskip\intertableskip -\def\jobscaption{Contractor Jobs} -\def\jobstabular{|lp{0.7in}@{\extracolsep{\fill}}p{4.95in}|} - \begin{table}[!h] \centering - \caption{\jobscaption} - \begin{tabular*}{\textwidth}{\jobstabular} + \caption{Freelance Projects} + \begin{tabular*}{\textwidth}{|l@{\extracolsep{\fill}}p{5.7in}|} \hline -Date & Client & Description \\ +Date & Description \\ \hline -2001-2002 & \href{http://www.atspraha.cz/}{Advanced\newline Telecom\newline Services} & - Mobile technologies +\hypertarget{captive}{2002-2003} & \href{https://www.jankratochvil.net/project/captive/}{Captive}: The first free NTFS read/write filesystem for Linux OS - \begin{itemizel} + Featured \href{https://tech.slashdot.org/story/03/12/02/1536227/using-the-real-ntfssys-driver-under-linux}{on slashdot}. - \item - MMS framework client and \href{http://www.jankratochvil.net/etmms/}{pilot MMS Center debugging} - via EuroTel GSM operator by technologies GSM~MMS, Perl, XML, SOAP + Reverse engineered i386 MS-Windows XP kernel and ntfs.sys for the first + binary compatible reimplementation of the undocumented parts of MS-Windows + kernel subsystems. - \item - Multimedia EMS support implemented by \href{http://www.jankratochvil.net/project/gsmperl/}{GSM::SMS patch} + Userland part of the project provides \href{https://en.wikipedia.org/wiki/GnomeVFS}{GnomeVFS} interface to the MS-Windows kernel + based MS-Windows filesystem isolated by the + \href{https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture}{CORBA}/\href{https://en.wikipedia.org/wiki/ORBit}{ORBit} + interface. + + For development purposes I implemented also \href{https://www.jankratochvil.net/project/TraceFS/}{TraceFS}: + MS-Windows Kernel API Tracer as a~native MS-Windows kernel driver for + tracing of the NT Cache Manager. + \\ +\hline - \item - Implementation of protocol BIP (based on Nokia CIMD2 protocol) for GSM - SMS Center direct communication by extending gnokii - (\href{http://www.gnokii.org/}{external link}) for EuroTel GSM operator - \end{itemizel} +\hypertarget{mms2}{2005-2006} & \href{https://www.jankratochvil.net/product/mms2/}{MMS2}: Alternative center for cheaper mobile phones multimedia messages (MMS) + \\ +\hline + +2004 & \href{https://www.jankratochvil.net/product/hotelgate/}{HotelGate}: Internet gateway for hotel guests \\ \hline \end{tabular*} \end{table} -\pagebreak +\vskip\intertableskip -\begin{table}[!h] +\begin{table}[!ht] \centering - \caption{\jobscaption{ }(continued)} - \begin{tabular*}{\textwidth}{\jobstabular} + \caption{Contractor Jobs} + \begin{tabular*}{\textwidth}{|lp{0.7in}@{\extracolsep{\fill}}p{4.95in}|} \hline Date & Client & Description \\ \hline -1999-2000 & \href{http://www.suse.com/}{SuSE} & - \href{http://www.jankratochvil.net/project/surprise/}{Partition Surprise}: GPLed Partition Magic clone - - Project was completed as a team work. The code is based on Gnome technologies. - - As one of the subtasks to keep system bootability I implemented - \href{http://www.jankratochvil.net/project/int13sniff/}{Int13Sniff} - to ease Microsoft boot loaders disassembly across partition modifications. +2001-2002 & \href{https://www.atspraha.cz/en/}{Advanced\newline Telecom\newline Services} & + Early cell phone technologies: + \href{https://en.wikipedia.org/wiki/Multimedia_Messaging_Service}{MMS} framework development incl. \href{https://www.jankratochvil.net/etmms/}{MMS Center debugging} + (written using \href{https://en.wikipedia.org/wiki/Perl}{Perl}, \href{https://en.wikipedia.org/wiki/XML}{XML}, \href{https://en.wikipedia.org/wiki/SOAP}{SOAP}), + Multimedia EMS support (my~\href{https://www.jankratochvil.net/project/gsmperl/}{GSM::SMS patch}), + BIP (Nokia CIMD2 alike) protocol client by extending (\href{https://www.gnokii.org/}{gnokii}). \\ \hline -2001-2002 & \href{http://www.readynote.com/}{ReadyNote} & - Mobile technologies for embedded devices +1999-2000 & \href{https://www.suse.com/}{SuSE} & + \href{https://www.jankratochvil.net/project/surprise/}{Partition Surprise}: superseded by \href{https://en.wikipedia.org/wiki/GParted}{GParted} - \begin{itemizel} - \item - Debugged gnokii (\href{http://www.gnokii.org/}{external link}) - GSM software modem for Nokia 5190 - - \item - Port of gnokii and my - \href{http://www.jankratochvil.net/project/mdsms/}{mdsms} (Mobile Device SMS Tool) - to embedded uClinux (\href{http://www.uclinux.org/}{external link}) - \end{itemizel} + It was implemented by multiple people of our team. For development purposes I~implemented + \href{https://www.jankratochvil.net/project/int13sniff/}{Int13Sniff} for bootability troubleshooting. \\ \hline -1999-2003 & \href{http://www.jklabs.cz/}{JKLabs} & - System administration of GNU/Linux and FreeBSD, techsupport: GuestNET, - \href{http://www.2m.dk/web/html_version/eclipse/eclipse.html}{Eclipse}, - \href{http://www.globaloop.com/}{GlobaLoop}, - \href{http://www.antlimited.com/products/fresco.htm}{Fresco} - - Ad hoc technical support challenges such as i386 IRQ redirector, - \href{http://www.jankratochvil.net/project/middleman/}{Middleman port} (HTTP proxy ported to FreeBSD) or - \href{http://www.jankratochvil.net/project/ssht/}{ssht} (Intranet accessibility by SSH tunnel). +2001-2002 & \href{https://www.readynote.com/}{ReadyNote} & + Early cell phone technologies: debugged \href{https://www.gnokii.org/}{gnokii} GSM software modem, ported \href{https://www.gnokii.org/}{gnokii} and my + \href{https://www.jankratochvil.net/project/mdsms/}{mdsms} to embedded \href{https://en.wikipedia.org/wiki/UClibc}{uClibc}. \\ \hline -2001 & \href{http://www.gtsgroup.cz/}{GTS} & - \href{http://www.jankratochvil.net/project/tac_plus/}{tac\_plus auth}: Cisco TACACS+ NAS host based authorization +1999-2003 & \href{https://jklabs.cz/}{JKLabs} & + Support of company systems, hotel installations, + technical challenges like an i386 IRQ redirector, + \href{https://www.jankratochvil.net/project/middleman/}{Middleman port} (HTTP modifying proxy) or + \href{https://www.jankratochvil.net/project/ssht/}{ssht} (SSH tunnel). \\ \hline -1999 & \href{http://www.unicom-prague.cz/}{Unicom} & - Technical translations from English +2001 & \href{https://www.gts.cz/cz-en}{GTS} & + \href{https://www.jankratochvil.net/project/tac_plus/}{tac\_plus auth}: Cisco TACACS+ NAS host based authorization \\ \hline -1998-1999 & \href{http://www.geoinvest.cz/}{Geoinvest} & - Mobile technologies, embedded devices - - \begin{itemizel} - \item - \href{http://www.jankratochvil.net/project/332/}{332}: AmigaOS kernel port to embedded Motorola 68332 - - \item - Siemens M1 GSM modem daemon for GPS, client/server setup, remote GSM terminals - \end{itemizel} +1998-1999 & \href{https://www.geoinvest.cz/}{Geoinvest} & + Early cell phone technologies -- Siemens M1 GSM modem daemon for GPS, + embedded devices -- \href{https://www.jankratochvil.net/project/332/}{332}: + \href{https://en.wikipedia.org/wiki/AmigaOS}{AmigaOS} kernel port to embedded \href{https://en.wikipedia.org/wiki/Freescale_683XX}{Motorola 68332}. \\ \hline -1999 & \href{http://www.tencom.cz/}{TENcom\newline Trade} & - Mobile technologies - - \begin{itemizel} - \item - \href{http://www.jankratochvil.net/project/mdsms/}{MobilDock SMS Tool}: - SMS communication software for Digital UNIX - \end{itemizel} +1999 & \href{https://www.tencom.cz/}{TENcom\newline Trade} & + Early cell phone technologies -- \href{https://www.jankratochvil.net/project/mdsms/}{mdsms}: + \href{https://en.wikipedia.org/wiki/SMS}{SMS} software for \href{https://en.wikipedia.org/wiki/Digital_UNIX}{Digital UNIX}. \\ \hline -1998 & \href{http://www.elsa-online.org/}{ELSA} & - \href{http://www.jankratochvil.net/project/step/}{STEP}: - Custom web database application incl. fixes of - \href{http://www.jankratochvil.net/project/phphash/}{PHP} and - \href{http://www.jankratochvil.net/project/pgsqlsubstr/}{PostgreSQL},\newline\hfil - for \href{http://www.elsa-online.org/}{ELSA -- European Law Students' Association}. +1998 & ELSA & + \href{https://www.jankratochvil.net/project/step/}{STEP}: + A~web database application. Fixed Free Software + \href{https://www.jankratochvil.net/project/phphash/}{PHP} and + \href{https://www.jankratochvil.net/project/pgsqlsubstr/}{PostgreSQL}. \\ \hline @@ -299,7 +259,7 @@ Date & Client & Description \\ \\ \hline -1993 & \href{http://www.japhila.cz/index_en.htm}{Japhila} & +1993 & \href{http://www.japhila.cz/}{Japhila} & StampMan: Database application (MS-DOS: Pascal, plainTeX) \\ \hline @@ -307,16 +267,18 @@ Date & Client & Description \\ \end{tabular*} \end{table} +\pagebreak + \begin{table}[!h] \centering \caption{Awards} \begin{tabular*}{\textwidth}{|l@{\extracolsep{\fill}}p{6.15in}|} \hline - 1997 & Bronze medal in the International Olympiad in Informatics, South Africa -- Cape Town \\ - 1997 & 2nd in an MO-P national contest -- mathematics Olympiad, the programming category \\ - 1997 & 1st in the International Competition in Programming \\ - 1996 & 4th in the International Competition in Programming \\ - 1995 & 7th in the Central European Olympiad in Informatics \\ + 1997 & Bronze medal (not 3rd) in International Olympiad in Informatics, South Africa -- Cape Town \\ + 1997 & 2nd in MO-P national contest -- mathematics Olympiad, the programming category \\ + 1997 & 1st in International Competition in Programming \\ + 1996 & 4th in International Competition in Programming \\ + 1995 & 7th in Central European Olympiad in Informatics \\ \hline \end{tabular*} \end{table} @@ -325,31 +287,28 @@ Date & Client & Description \\ \begin{table}[!h] \centering - \caption{Experience Summary} + \caption{Technologies (mostly obsolete in 2021)} \begin{tabular*}{\textwidth}{|l@{\extracolsep{\fill}}p{5.10in}|} \hline - Major Areas & mobile technologies, filesystems, embedded devices \\ - Platforms & GNU/Linux/i386/UNIX/FreeBSD, W32 kernel, Amiga/680x0, MS-DOS, - ZX Spectrum, handheld PC-E500S \\ - Languages & C (Gnome/GCC), Perl, bash/awk/sed..., - Java, C++, Pascal, Basic, REXX, S-Lang, Foxplus, Prolog \\ - Tools/Metalanguages & autoconf, automake, libtool, m4, CVS, gettext, bison, flex, ld~script, - gdb~script - %, Maple~V - \\ - Assemblers & i386/x86\_64, Motorola 680x0, Zilog~Z80, Intel 8051, MIPS R2/3000, - (Hitachi SH-8) \\ - WWW-Related & HTML/XHTML, CSS, PHP, SQL (PostgreSQL, MySQL), JavaScript, CGI \\ +% Major Areas & mobile technologies, filesystems, embedded devices \\ + Platforms & Linux OS/i386/FreeBSD, MS-Windows i386 kernel, Amiga/680x0, MS-DOS, + ZX Spectrum, PC-E500S \\ + Languages & C++14, C, Perl, Python, bash/awk/sed..., + Java$\le$1.0, Pascal, Basic, REXX, S-Lang, Foxplus, Prolog \\ + Tools & GIT, cmake, SVN, autoconf, automake, libtool, m4, CVS, gettext, bison, flex, ld~script \\ + Assemblers & i386/x86\_64, briefly aarch64/arm7/s390x/ppc64, Motorola 680x0, Zilog~Z80, Intel 8051, MIPS R2/3000 \\ + WWW & HTML$\le$4.0, CSS, PHP, SQL (PostgreSQL, MySQL), JavaScript, CGI \\ Documentation & plainTeX, LaTeX, DocBook, DocBook Lite, XML/NS/Schema, gtk-doc, nroff, pod \\ Libraries & Gnome/GTK+/GLib, POSIX/BSD/SysV/threads/sockets, X11, OpenGL, NCurses, - S-Lang, GnomeVFS, libxml, libneon, SVGALib, NIS, Qt,... \\ - RPC & CORBA/ORBit, SOAP, WSDL, Sun~RPC \\ - Protocols & GSM SMS/PDU/Nokia~Smart~Messaging/EMS/WAP/WTP/WSP/MMSE/SMIL,\hfill\newline + S-Lang, GnomeVFS, libxml, libneon, SVGALib, NIS, Qt, + % RPC + CORBA/ORBit, SOAP, WSDL, Sun~RPC \\ + Protocols/Formats & GSM SMS/PDU/Nokia~Smart~Messaging/EMS/WAP/WTP/WSP/MMSE/SMIL,\hfill\newline WML/AMR/WSP/AMR, GSM ETSI/3GPP stds, IPv4, HTTP, SMTP\hfill\newline - RFC822/MIME, FTP, SSH, DHCP, POP3, IMAP4, NFS~v2/v3, PPP/LCP/IPCP,... \\ - Filesystems & ext2, FAT, AmigaFFS, ISO-9660 \\ - Certificates & \href{https://www.redhat.com/wapps/training/certification/verify.html?certNumber=110-518-099}{RHCE 110-518-099} \\ + RFC822/MIME, FTP, SSH, DHCP, POP3, IMAP4, NFS~v2/v3, PPP/LCP/IPCP, + % Filesystems + ext2, FAT, AmigaFFS, ISO-9660 \\ \hline \end{tabular*} \end{table} diff --git a/resume/ResumeJanKratochvil.txt b/resume/ResumeJanKratochvil.txt deleted file mode 100644 index bc0a283..0000000 --- a/resume/ResumeJanKratochvil.txt +++ /dev/null @@ -1,160 +0,0 @@ -Name: Jan Kratochvil -English: technical: fluent, general: communicable -Born: 1979 -Sex: male -eMail: job@jankratochvil.net -Projects: http://www.jankratochvil.net/project/ -Resume: http://www.jankratochvil.net/resume/ -OpenPGP: 8A48C6AC - - -Fulltime Jobs -------------- - -2006/07-...: Red Hat - LLDB development, ELF, DWARF, ptrace, unwinding, ABI, formerly GDB development - -2005/04-11: VA Linux Systems Japan - Linux kernel based development - -2004/01-11: Sun Microsystems - Java Enterprise System Quality Assurance (JES QA), technical lead - JES components compatibility analysis, automation of QA tasks, JES testing, - QA frameworks pilot deployments, architecture and lead of the team projects, - team programming guidelines - - -Freelance Commercial Projects ------------------------------ - -2005-2006: MMS2: Alternative center for mobile phones multimedia messages (MMS) - Web interface was outsourced. - Commercial service management held together by company JK-Invent. - * Designed to coexist with existing GSM networks service servers - * MMSE/WAP-Push/web/mail convergency featuring smart transformations - * Credits charging by ISDN IVR + automatic bank transaction acceptance - * Completed mobile phones firmware bugs analysis and workarounds - * Free gatewaying program to avoid targetted GSM operators firewalling - -2004: HotelGate: Internet Public Access Gateway - Plug&Play TCP/IP for clients, selectively ticketed and/or free access, - simplified interface for reception desks, easy and unified management. - European facility installations provided by JK Labs. - -2002-2003: Captive: The first free NTFS read/write filesystem for GNU/Linux - Featured on slashdot. - Analysis of portions of 80MB+ i386 disassembly dumps of MS-Windows XP kernel - and ntfs.sys for the first binary compatible reimplementation of the - undocumented XP kernel subsystems. - Written TraceFS: Microsoft Windows Kernel API Tracer: MS-Windows Kernel API - Tracer as native W32 kernel hooking driver for run-time analysis of the NT - Cache Manager XP kernel subsystem. - UNIX userland of Captive provides GnomeVFS interface to the MS-Windows kernel - based MS-Windows filesystem isolated by the CORBA/ORBit interface. - - -Contractor Jobs ---------------- - -2001-2002: Advanced Telecom Services - Mobile technologies - * MMS framework client via EuroTel GSM operator - by technologies GSM MMS, Perl, XML, SOAP - Project included pilot MMS Center debugging with Ethereal, - fixes of Ethereal. - * Multimedia EMS support implemented by GSM::SMS patch - * Implementation of protocol BIP (based on Nokia CIMD2 protocol) for GSM SMS - Center direct communication by extending gnokii for EuroTel GSM operator - -1999-2000: SuSE - Partition Surprise: GPLed Partition Magic clone - Project was completed as a team work. The code is based on Gnome - technologies. As one of the subtasks to keep system bootability I implemented - Int13Sniff to ease Microsoft boot loaders disassembly across partition - modifications. - -2001-2002: ReadyNote - Mobile technologies for embedded devices - * Completed debugging of gnokii GSM software modem for Nokia 5190 - * Port of gnokii and my mdsms (Mobile Device SMS Tool) to embedded uClinux - -1999-2003: JKLabs - System administration of GNU/Linux and FreeBSD, - techsupport: GuestNET, Eclipse, GlobaLoop, Fresco - Ad hoc technical support challenges such as i386 IRQ redirector, Middleman - port (HTTP proxy ported to FreeBSD) or ssht (Unattended intranet host - accessibility by SSH tunnel). - -2001: GTS - tac_plus auth: Cisco TACACS+ NAS host based authorization - -1999: Unicom - Technical translations from English - -1998-1999: Geoinvest - Mobile technologies, embedded devices - * 332: AmigaOS kernel port to embedded Motorola 68332 - * Siemens M1 GSM modem daemon for GPS, client/server setup, - remote GSM terminals - -1999: TENcom Trade - Mobile technologies - * MobilDock SMS Tool: SMS communication software for Digital UNIX - -1998: European Law Students' Association - STEP: Custom web database application incl. fixes of PHP and PostgreSQL - -1993-1996: Profes J&K - PC assembly and customer service - -1993: Japhila - StampMan: Database application (MS-DOS: Pascal, plainTeX) - - -Awards ------- - -1997: Bronze medal in the International Olympiad in Informatics, Cape Town -1997: 2nd in an MO-P national contest - mathematics Olympiad, the programming -1997: 1st in the International Competition in Programming -1996: 4th in the International Competition in Programming -1995: 7th in the Central European Olympiad in Informatics - - -Experience Summary ------------------- - -Major Areas: - mobile technologies, filesystems, embedded devices -Platforms: - GNU/Linux/i386/UNIX/FreeBSD, W32 kernel, Amiga/680x0, MS-DOS, ZX Spectrum, - handheld PC-E500S -Languages: - C (Gnome/GCC), Perl, bash/awk/sed..., Java, C++, Pascal, Basic, REXX, S-Lang, - Foxplus, Prolog -Tools/Metalanguages: - autoconf, automake, libtool, m4, CVS, gettext, bison, flex, ld script, - gdb script, Maple V -Assemblers: - i386/x86_64, Motorola 680x0, Zilog Z80, Intel 8051, MIPS R2/3000, - (Hitachi SH-8) -WWW-Related: - HTML/XHTML, CSS, PHP, SQL (PostgreSQL, MySQL), JavaScript, CGI -Documentation: - plainTeX, LaTeX, DocBook, DocBook Lite, XML/NS/Schema, gtk-doc, nroff, pod -Libraries: - Gnome/GTK+/GLib, POSIX/BSD/SysV/threads/sockets, X11, OpenGL, NCurses, - S-Lang, GnomeVFS, libxml, libneon, SVGALib, NIS, Qt,... -RPC: - CORBA/ORBit, SOAP, WSDL, Sun RPC -Protocols: - GSM SMS/PDU/Nokia Smart Messaging/EMS/WAP/WTP/WSP/MMSE/SMIL/WML/AMR/ETSI - stds/3GPP stds, IPv4, HTTP, SMTP/RFC822/MIME, FTP, SSH, DHCP, POP3, IMAP4, - NFS v2/v3, PPP/LCP/IPCP,... -Filesystems: - ext2, FAT, AmigaFFS, ISO-9660 -Certificates: - RHCE 110-518-099 - - -$Id$ diff --git a/resume/devconf2017.pdf b/resume/devconf2017.pdf new file mode 100644 index 0000000..2b9ac67 Binary files /dev/null and b/resume/devconf2017.pdf differ diff --git a/resume/devconf2019.pdf b/resume/devconf2019.pdf new file mode 100644 index 0000000..72cfddf Binary files /dev/null and b/resume/devconf2019.pdf differ diff --git a/resume/gdb2012.pdf b/resume/gdb2012.pdf new file mode 100644 index 0000000..dc169ae Binary files /dev/null and b/resume/gdb2012.pdf differ