001 #!/usr/bin/perl 002 #It's already too late to do that check here, but at least try to print a 003 #warning 004 if ($> != $<) 005 { 006 print "Must not run with Set-UID bit set\n"; 007 exit; 008 } 009 use English; 010 use Socket; 011 use Fcntl qw/O_RDWR O_CREAT O_NOFOLLOW/; 012 ($err_o_nofollow, $MY_O_NOFOLLOW) = Fcntl::constant("O_NOFOLLOW"); 013 if ($err_o_nofollow ne "") { $MY_O_NOFOLLOW = 0; } 014 use POSIX qw(SEEK_SET SEEK_CUR SEEK_END); 015 sub bynum {$a <=> $b;} 016 sub childhnd { 017 $childs--; 018 wait(); 019 return; 020 } 021 sub timeout { 022 exit(0); 023 } 024 sub dprint { 025 my($line)=@_; 026 unless ($SILENT) 027 { 028 print "$line"; 029 } 030 } 031 if ($ARGV[0] =~ /^silent$/i) {$SILENT=1;} 032 $|=1; 033 #We need to be root at startup so we can bind to the ident port. 034 if ($< != 0) 035 { 036 print "Need to be root to bind to the ident port\n"; 037 exit; 038 } 039 chdir("/") or die "Can not change working directory"; 040 &dprint("\n***** Random Ident Server 0.9.3b *****\n"); 041 #We first gather our words, this takes some time. If we want to drop privileges 042 #on restart, we would need to kill the old process before binding to the socket 043 #and droping privileges. For this reason we gather words before we drop privileges 044 #as doing so after would leave a large timegap for the service when restarted. 045 opendir(ISPELL,"/usr/lib/ispell")|| die "No /usr/lib/ispell dir found\n"; 046 @files=readdir(ISPELL); 047 closedir(ISPELL); 048 $index=0; 049 while (($index <= $#files)&&(!$usefile)) 050 { 051 $file=$files[$index]; 052 # Note: As the hash filename is passed to /usr/bin/strings through a shell, 053 # we reject any filenames that begin with '-' (interpreted as an option by 054 # strings) or that contain a single quote (as we quote the filename in single 055 # quotes this would end the quoting). Aside note: the regexp below does not 056 # allow the filename ".hash" either, but we do not care about this. 057 if ($file =~ /^[^-'][^']*\.hash$/) 058 { 059 $usefile=$file; 060 } 061 $index++; 062 } 063 unless ($usefile) {print "No hash file found in /usr/lib/ispell\n";exit;} 064 &dprint("* Using file /usr/lib/ispell/'$usefile'\n"); 065 &dprint("* Counting usable words\n"); 066 open(STR1,"/usr/bin/strings /usr/lib/ispell/'$usefile'|")||die "Can't start strings"; 067 while() 068 { 069 chomp(); 070 chomp(); 071 $line=lc($_); 072 $len=length($line); 073 if (($line =~ /^[a-z]+$/)&&($len > 4) && ($len < 13)) 074 { 075 $maxword++; 076 if (($maxword % 40) == 0) { &dprint("\r working: | ($maxword)")}; 077 if (($maxword % 40) == 10) { &dprint("\r working: / ($maxword)")}; 078 if (($maxword % 40) == 20) { &dprint("\r working: - ($maxword)")}; 079 if (($maxword % 40) == 30) { &dprint("\r working: \\ ($maxword)")}; 080 } 081 } 082 &dprint("\r \r"); 083 close(STR1); 084 $count=0; 085 $maxcount=1024+int(rand(2048)); 086 &dprint("* Generating random selection of $maxcount from $maxword\n"); 087 $setsize=$maxword; 088 @fetched=(); 089 $fnum=0; 090 while($fnum < $maxcount) 091 { 092 $rnum=int(rand($setsize)); 093 foreach $old (@fetched) 094 { 095 if ($old <= $rnum) {$rnum++;} 096 } 097 $continue=1; 098 while($continue) 099 { 100 $continue=0; 101 foreach $old (@fetched) 102 { 103 if ($old == $rnum) 104 { 105 $rnum++; 106 $continue=1; 107 } 108 } 109 } 110 $fetched[$fnum]=$rnum; 111 $fnum++; 112 $pnum=int(($fnum*$fnum*1000)/($maxcount*$maxcount))/10; 113 if (($fnum %8)==0) {&dprint("\r working: | ($pnum \%)");} 114 if (($fnum %8)==2) {&dprint("\r working: / ($pnum \%)");} 115 if (($fnum %8)==4) {&dprint("\r working: - ($pnum \%)");} 116 if (($fnum %8)==6) {&dprint("\r working: \\ ($pnum \%)");} 117 } 118 &dprint("\r \r"); 119 &dprint("* Generating insertion table\n"); 120 foreach $index (0 .. $#fetched) 121 { 122 $inserttable{$fetched[$index]}=$index; 123 } 124 open(STR1,"/usr/bin/strings /usr/lib/ispell/'$usefile'|")||die "Can't start strings"; 125 &dprint("* Acquiring names\n"); 126 $count=0; 127 while() 128 { 129 chomp(); 130 chomp(); 131 $line=lc($_); 132 $len=length($line); 133 if (($line =~ /^[a-z]+$/)&&($len > 4) && ($len < 13)) 134 { 135 if (defined($inserttable{$count})) 136 { 137 $words[$inserttable{$count}] =$line; 138 } 139 $count++; 140 $countp=int(1000*$count/$maxword)/10; 141 if (($count % 40) == 0) { &dprint("\r working: | ($countp \%)")}; 142 if (($count % 40) == 10) { &dprint("\r working: / ($countp \%)")}; 143 if (($count % 40) == 20) { &dprint("\r working: - ($countp \%)")}; 144 if (($count % 40) == 30) { &dprint("\r working: \\ ($countp \%)")}; 145 } 146 } 147 &dprint("\r \r"); 148 &dprint("* $maxcount words fetched\n"); 149 &dprint(" (local ports belonging to each unique modulus of $maxcount get\n"); 150 &dprint(" a dictionary word assigned to them)\n"); 151 152 ($pnam,$dummy,$myuid,$mygid)=getpwnam("nobody"); 153 unless (($myuid) && ($mygid)) 154 { 155 print "No user nobody defined\n"; 156 exit; 157 } 158 #Just in case O_NOFOLLOW is not supported we double check. 159 if (-l "/tmp/rident.pid") { 160 print "ERROR: It apears someone has tampered with the pid file /tmp/rident.pid (test1)\n"; 161 exit; 162 } elsif (sysopen(PID,"/tmp/rident.pid",O_RDONLY | $MY_O_NOFOLLOW)) { 163 $oldpid = 0; 164 ($pfuid,$pfgid) = (stat(PID))[4,5] or die "PANIC: Can not stat open pidfile"; 165 unless ($pfuid == $myuid || $pfgid == $mygid) 166 { 167 print "ERROR: It apears someone has tampered with the pid file /tmp/rident.pid (test2)\n"; 168 exit; 169 } else { 170 #See if the file was already written. 171 seek(PID,0,SEEK_END); 172 $size=tell(PID); 173 seek(PID,0,SEEK_SET); 174 #If it was read the old pid. 175 if ($size > 0 && $size < 100) { 176 &dprint("* PID file found\n"); 177 sysread(PID,$oldpid,$size,0); 178 chomp $oldpid; 179 unless ($oldpid =~ /^\d+$/) 180 { 181 print "ERROR: It apears someone has tampered with the pid file /tmp/rident.pid (test3)\n"; 182 exit; 183 } 184 $oldpid +=0; 185 } else { 186 &dprint("* PID file is corrupt\n"); 187 exit; 188 } 189 } 190 close(PID); 191 #Now let's test if the found pid is really our old instance. 192 $killit=0; 193 if ($oldpid) 194 { 195 &dprint("* Checking for process with pid $oldpid\n"); 196 open(PS,"/bin/ps -p '$oldpid'|"); 197 $killit=0; 198 while () 199 { 200 if (/\bridentd\.pl\b/) 201 { 202 $killit=$oldpid; 203 } 204 } 205 unless ($killit) 206 { 207 &dprint("* pid file apears to be old\n"); 208 } else { 209 #We need to kill the old process, but we are root and thus to powerfull. 210 $tpid=fork(); 211 unless (defined($tpid)) { 212 print "ERR: Forking error\n"; 213 exit; 214 } 215 if ($tpid) { 216 #The parent remains root and must wait for the child process. 217 &dprint("* Started background process to kill old instance safely\n"); 218 waitpid($tpid,0); 219 &dprint("* Background process finished.\n"); 220 sleep(1); 221 } else { 222 unless ($)=$mygid) {print "Cleanup Unable to set group ID to $mygid\n";exit;} 223 unless ($>=$myuid) {print "Cleanup Unable to set user ID to $myuid\n";exit;} 224 kill(9,$killit); 225 exit; 226 } 227 } 228 } 229 } 230 &dprint("* Binding to port 113\n"); 231 $port=113; 232 $proto=getprotobyname('tcp'); 233 socket(Server,PF_INET,SOCK_STREAM,$proto)|| die "socket $!"; 234 setsockopt(Server,SOL_SOCKET,SO_REUSEADDR,pack("l",1))|| die "setsockopt $!"; 235 bind(Server,sockaddr_in($port,INADDR_ANY))|| die "bind: $!"; 236 listen(Server,SOMAXCON)||die "listen: $!"; 237 unlink("/tmp/rident.pid"); 238 &dprint("* Setting uid/gid to nobody $myuid/$mygid\n"); 239 unless ($)=$mygid) { print "Unable to set group ID to $mygid\n"; exit; } 240 unless ($>=$myuid) { print "Unable to set user ID to $myuid\n"; exit; } 241 unless (sysopen(PID,"/tmp/rident.pid",O_RDWR | O_CREAT | O_EXCL, 0600)) { 242 print "PANIC: Could not create the pid file /tmp/rident.pid $! $?\n"; 243 exit; 244 } 245 &dprint("* Forking to background\n"); 246 $pid=fork(); 247 unless (defined($pid)) { 248 print "ERR: Forking error\n"; 249 close(PID); 250 exit; 251 } 252 if ($pid) 253 { 254 # parent 255 unless (syswrite(PID,"$pid\n",length("$pid\n"))) { 256 print "PANIC: Could not write to the pid file /tmp/rident.pid $! $?\n"; 257 close(PID); 258 kill(9,$pid); 259 } 260 exit; 261 } 262 close(PID); 263 &dprint("* BG Process active\n"); 264 &dprint("* Checking whether we can do socketpair lookups : "); 265 if (-r "/proc/net/tcp") { 266 &dprint("Yep: LINUX\n"); 267 $LOOKUP="LINUX"; 268 } 269 else { 270 &dprint("NO !!!\n"); 271 $LOOKUP=0; 272 } 273 $childs=0; 274 $SIG{'CHLD'}='childhnd'; 275 while(1) { 276 if ($paddr = accept(Client,Server)) 277 { 278 ($remoteport,$remoteip)=sockaddr_in($paddr); 279 $remoteip=unpack("L",$remoteip); 280 #Limmited forking and extra sleeping, better for the service than the system to be dossed 281 if ($childs < 10) 282 { 283 $pid=fork(); 284 unless (defined($pid)) { 285 print "ERR: Forking error\n"; 286 exit; 287 } 288 if ($pid==0) 289 { 290 select Client;$|=1;select(STDOUT); 291 $SIG{"ALARM"}='timeout'; 292 alarm(10); 293 $firstline=; 294 if ($firstline =~ /(\d+)\s*,\s*(\d*)/) 295 { 296 $port=$1; 297 $port2=$2; 298 $found=1; 299 #If there is no portablility for the lookup just skip it 300 if ($LOOKUP eq "LINUX") 301 { 302 $found=0; 303 $rip=sprintf("%lX",$remoteip); 304 while (length($rip) < 8) {$rip = "0$rip";} 305 $rprt=sprintf("%X",$port2); 306 while (length($rprt) < 4) {$rprt = "0$rprt";} 307 $lprt=sprintf("%X",$port); 308 while (length($lprt) < 4) {$lprt = "0$lprt";} 309 open(TCPFIL,"/proc/net/tcp"); 310 while() 311 { 312 if (/:$lprt\s+${rip}:$rprt\s+/) 313 { 314 $found=1; 315 } 316 } 317 } 318 if ($found) 319 { 320 $word=$words[$port % $maxcount]; 321 print Client "${port},${port2}:USERID:OTHER:$word\n"; 322 } 323 else 324 { 325 print Client "${port},${port2}:ERROR:NO-USER\n"; 326 } 327 } 328 close(Client); 329 sleep(2); 330 exit; 331 } 332 else 333 { 334 $childs++; 335 close(Client); 336 } 337 } 338 else 339 { 340 print Client "0,0:ERROR:UNKNOWN-ERROR\n"; 341 close(Client); 342 } 343 } 344 }