1#!/usr/bin/perl -w 2#- 3# Copyright (c) 2002-2003 Networks Associates Technology, Inc. 4# All rights reserved. 5# 6# This software was developed for the FreeBSD Project by ThinkSec AS and 7# Network Associates Laboratories, the Security Research Division of 8# Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 9# ("CBOSS"), as part of the DARPA CHATS research program. 10# 11# Redistribution and use in source and binary forms, with or without 12# modification, are permitted provided that the following conditions 13# are met: 14# 1. Redistributions of source code must retain the above copyright 15# notice, this list of conditions and the following disclaimer. 16# 2. Redistributions in binary form must reproduce the above copyright 17# notice, this list of conditions and the following disclaimer in the 18# documentation and/or other materials provided with the distribution. 19# 3. The name of the author may not be used to endorse or promote 20# products derived from this software without specific prior written 21# permission. 22# 23# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33# SUCH DAMAGE. 34# 35# $P4: //depot/projects/openpam/misc/gendoc.pl#25 $ 36# 37 38use strict; 39use Fcntl; 40use Getopt::Std; 41use POSIX qw(strftime); 42use vars qw($COPYRIGHT $TODAY %FUNCTIONS %PAMERR); 43 44$COPYRIGHT = ".\\\"- 45.\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc. 46.\\\" All rights reserved. 47.\\\" 48.\\\" This software was developed for the FreeBSD Project by ThinkSec AS and 49.\\\" Network Associates Laboratories, the Security Research Division of 50.\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 51.\\\" (\"CBOSS\"), as part of the DARPA CHATS research program. 52.\\\" 53.\\\" Redistribution and use in source and binary forms, with or without 54.\\\" modification, are permitted provided that the following conditions 55.\\\" are met: 56.\\\" 1. Redistributions of source code must retain the above copyright 57.\\\" notice, this list of conditions and the following disclaimer. 58.\\\" 2. Redistributions in binary form must reproduce the above copyright 59.\\\" notice, this list of conditions and the following disclaimer in the 60.\\\" documentation and/or other materials provided with the distribution. 61.\\\" 3. The name of the author may not be used to endorse or promote 62.\\\" products derived from this software without specific prior written 63.\\\" permission. 64.\\\" 65.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 66.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 67.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 68.\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 69.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 70.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 71.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 72.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 73.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 74.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 75.\\\" SUCH DAMAGE. 76.\\\" 77.\\\" \$" . "P4" . "\$ 78.\\\""; 79 80%PAMERR = ( 81 PAM_SUCCESS => "Success", 82 PAM_OPEN_ERR => "Failed to load module", 83 PAM_SYMBOL_ERR => "Invalid symbol", 84 PAM_SERVICE_ERR => "Error in service module", 85 PAM_SYSTEM_ERR => "System error", 86 PAM_BUF_ERR => "Memory buffer error", 87 PAM_CONV_ERR => "Conversation failure", 88 PAM_PERM_DENIED => "Permission denied", 89 PAM_MAXTRIES => "Maximum number of tries exceeded", 90 PAM_AUTH_ERR => "Authentication error", 91 PAM_NEW_AUTHTOK_REQD => "New authentication token required", 92 PAM_CRED_INSUFFICIENT => "Insufficient credentials", 93 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable", 94 PAM_USER_UNKNOWN => "Unknown user", 95 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials", 96 PAM_CRED_EXPIRED => "User credentials have expired", 97 PAM_CRED_ERR => "Failed to set user credentials", 98 PAM_ACCT_EXPIRED => "User accound has expired", 99 PAM_AUTHTOK_EXPIRED => "Password has expired", 100 PAM_SESSION_ERR => "Session failure", 101 PAM_AUTHTOK_ERR => "Authentication token failure", 102 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token", 103 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy", 104 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled", 105 PAM_NO_MODULE_DATA => "Module data not found", 106 PAM_IGNORE => "Ignore this module", 107 PAM_ABORT => "General failure", 108 PAM_TRY_AGAIN => "Try again", 109 PAM_MODULE_UNKNOWN => "Unknown module type", 110 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain", 111); 112 113sub parse_source($) { 114 my $fn = shift; 115 116 local *FILE; 117 my $source; 118 my $func; 119 my $descr; 120 my $type; 121 my $args; 122 my $argnames; 123 my $man; 124 my $inlist; 125 my $inliteral; 126 my %xref; 127 my @errors; 128 129 if ($fn !~ m,\.c$,) { 130 warn("$fn: not C source, ignoring\n"); 131 return undef; 132 } 133 134 sysopen(FILE, $fn, O_RDONLY) 135 or die("$fn: open(): $!\n"); 136 $source = join('', <FILE>); 137 close(FILE); 138 139 return undef 140 if ($source =~ m/^ \* NOPARSE\s*$/m); 141 142 $func = $fn; 143 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,; 144 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) { 145 warn("$fn: can't find $func\n"); 146 return undef; 147 } 148 ($descr, $type, $args) = ($1, $2, $3); 149 $descr =~ s,^([A-Z][a-z]),lc($1),e; 150 $descr =~ s,[\.\s]*$,,; 151 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) { 152 # nothing 153 } 154 $args =~ s/,\s+/, /gs; 155 $args = "\"$args\""; 156 157 %xref = ( 158 "pam 3" => 1 159 ); 160 161 if ($type eq "int") { 162 foreach (split("\n", $source)) { 163 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/); 164 push(@errors, $1); 165 } 166 $xref{"pam_strerror 3"} = 1; 167 } 168 169 $argnames = $args; 170 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g; 171 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g; 172 $argnames =~ s/\" \"/|/g; 173 $argnames =~ s/^\"(.*)\"$/($1)/; 174 $inliteral = $inlist = 0; 175 foreach (split("\n", $source)) { 176 s/\s*$//; 177 if (!defined($man)) { 178 if (m/^\/\*\*$/) { 179 $man = ""; 180 } 181 next; 182 } 183 last if (m/^ \*\/$/); 184 s/^ \* ?//; 185 s/\\(.)/$1/gs; 186 if (m/^$/) { 187 if ($man ne "" && $man !~ m/\.Pp\n$/s) { 188 if ($inliteral) { 189 $man .= "\0\n"; 190 } elsif ($inlist) { 191 $man .= ".El\n.Pp\n"; 192 $inlist = 0; 193 } else { 194 $man .= ".Pp\n"; 195 } 196 } 197 next; 198 } 199 if (m/^>(\w+)(?:\s+(\d))?$/) { 200 ++$xref{$2 ? "$1 $2" : "$1 3"}; 201 next; 202 } 203 if (s/^\s+(=?\w+):\s*/.It $1/) { 204 if ($inliteral) { 205 $man .= ".Ed\n"; 206 $inliteral = 0; 207 } 208 if (!$inlist) { 209 $man =~ s/\.Pp\n$//s; 210 $man .= ".Bl -tag -width 18n\n"; 211 $inlist = 1; 212 } 213 s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs; 214 $man .= "$_\n"; 215 next; 216 } elsif ($inlist && m/^\S/) { 217 $man .= ".El\n.Pp\n"; 218 $inlist = 0; 219 } elsif ($inliteral && m/^\S/) { 220 $man .= ".Ed\n"; 221 $inliteral = 0; 222 } elsif ($inliteral) { 223 $man .= "$_\n"; 224 next; 225 } elsif ($inlist) { 226 s/^\s+//; 227 } elsif (m/^\s+/) { 228 $man .= ".Bd -literal\n"; 229 $inliteral = 1; 230 $man .= "$_\n"; 231 next; 232 } 233 s/\s*=$func\b\s*/\n.Nm\n/gs; 234 s/\s*=$argnames\b\s*/\n.Va $1\n/gs; 235 s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs; 236 s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs; 237 s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs; 238 while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) { 239 ++$xref{"$1 3"}; 240 } 241 s/\s*\"(?=\w)/\n.Do\n/gs; 242 s/\"(?!\w)\s*/\n.Dc\n/gs; 243 s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs; 244 s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs; 245 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs; 246 $man .= "$_\n"; 247 } 248 if (defined($man)) { 249 if ($inlist) { 250 $man .= ".El\n"; 251 } 252 if ($inliteral) { 253 $man .= ".Ed\n"; 254 } 255 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs; 256 $man =~ s/\s*$/\n/gm; 257 $man =~ s/\n+/\n/gs; 258 $man =~ s/\0//gs; 259 $man =~ s/\n\n\./\n\./gs; 260 chomp($man); 261 } else { 262 $man = "No description available."; 263 } 264 265 $FUNCTIONS{$func} = { 266 'source' => $fn, 267 'name' => $func, 268 'descr' => $descr, 269 'type' => $type, 270 'args' => $args, 271 'man' => $man, 272 'xref' => \%xref, 273 'errors' => \@errors, 274 }; 275 if ($source =~ m/^ \* NODOC\s*$/m) { 276 $FUNCTIONS{$func}->{'nodoc'} = 1; 277 } 278 if ($source !~ m/^ \* XSSO \d/m) { 279 $FUNCTIONS{$func}->{'openpam'} = 1; 280 } 281 expand_errors($FUNCTIONS{$func}); 282 return $FUNCTIONS{$func}; 283} 284 285sub expand_errors($); 286sub expand_errors($) { 287 my $func = shift; # Ref to function hash 288 289 my %errors; 290 my $ref; 291 my $fn; 292 293 if (defined($func->{'recursed'})) { 294 warn("$func->{'name'}(): loop in error spec\n"); 295 return qw(); 296 } 297 $func->{'recursed'} = 1; 298 299 foreach (@{$func->{'errors'}}) { 300 if (m/^(PAM_[A-Z_]+)$/) { 301 if (!defined($PAMERR{$1})) { 302 warn("$func->{'name'}(): unrecognized error: $1\n"); 303 next; 304 } 305 $errors{$1} = 1; 306 } elsif (m/^!(PAM_[A-Z_]+)$/) { 307 # treat negations separately 308 } elsif (m/^=([a-z_]+)$/) { 309 $ref = $1; 310 if (!defined($FUNCTIONS{$ref})) { 311 $fn = $func->{'source'}; 312 $fn =~ s/$func->{'name'}/$ref/; 313 parse_source($fn); 314 } 315 if (!defined($FUNCTIONS{$ref})) { 316 warn("$func->{'name'}(): reference to unknown $ref()\n"); 317 next; 318 } 319 foreach (@{$FUNCTIONS{$ref}->{'errors'}}) { 320 $errors{$_} = 1; 321 } 322 } else { 323 warn("$func->{'name'}(): invalid error specification: $_\n"); 324 } 325 } 326 foreach (@{$func->{'errors'}}) { 327 if (m/^!(PAM_[A-Z_]+)$/) { 328 delete($errors{$1}); 329 } 330 } 331 delete($func->{'recursed'}); 332 $func->{'errors'} = [ sort(keys(%errors)) ]; 333} 334 335sub gendoc($) { 336 my $func = shift; # Ref to function hash 337 338 local *FILE; 339 my $mdoc; 340 my $fn; 341 342 return if defined($func->{'nodoc'}); 343 344 $mdoc = "$COPYRIGHT 345.Dd $TODAY 346.Dt " . uc($func->{'name'}) . " 3 347.Os 348.Sh NAME 349.Nm $func->{'name'} 350.Nd $func->{'descr'} 351.Sh LIBRARY 352.Lb libpam 353.Sh SYNOPSIS 354.In sys/types.h 355.In security/pam_appl.h 356"; 357 if ($func->{'name'} =~ m/_sm_/) { 358 $mdoc .= ".In security/pam_modules.h\n" 359 } 360 if ($func->{'name'} =~ m/openpam/) { 361 $mdoc .= ".In security/openpam.h\n" 362 } 363 $mdoc .= ".Ft $func->{'type'} 364.Fn $func->{'name'} $func->{'args'} 365.Sh DESCRIPTION 366$func->{'man'} 367"; 368 if ($func->{'type'} eq "int") { 369 $mdoc .= ".Sh RETURN VALUES 370The 371.Nm 372function returns one of the following values: 373.Bl -tag -width 18n 374"; 375 my @errors = @{$func->{'errors'}}; 376 warn("$func->{'name'}(): no error specification\n") 377 unless(@errors); 378 foreach (@errors) { 379 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; 380 } 381 $mdoc .= ".El\n"; 382 } else { 383 if ($func->{'type'} =~ m/\*$/) { 384 $mdoc .= ".Sh RETURN VALUES 385The 386.Nm 387function returns 388.Dv NULL 389on failure. 390"; 391 } 392 } 393 $mdoc .= ".Sh SEE ALSO\n"; 394 my @xref = sort(keys(%{$func->{'xref'}})); 395 while (@xref) { 396 $mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n"); 397 } 398 $mdoc .= ".Sh STANDARDS\n"; 399 if ($func->{'openpam'}) { 400 $mdoc .= "The 401.Nm 402function is an OpenPAM extension. 403"; 404 } else { 405 $mdoc .= ".Rs 406.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 407.%D \"June 1997\" 408.Re 409"; 410 } 411 $mdoc .= ".Sh AUTHORS 412The 413.Nm 414function and this manual page were developed for the FreeBSD Project 415by ThinkSec AS and Network Associates Laboratories, the Security 416Research Division of Network Associates, Inc. under DARPA/SPAWAR 417contract N66001-01-C-8035 418.Pq Dq CBOSS , 419as part of the DARPA CHATS research program. 420"; 421 422 $fn = "$func->{'name'}.3"; 423 if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) { 424 print(FILE $mdoc); 425 close(FILE); 426 } else { 427 warn("$fn: open(): $!\n"); 428 } 429} 430 431sub readproto($) { 432 my $fn = shift; # File name 433 434 local *FILE; 435 my %func; 436 437 sysopen(FILE, $fn, O_RDONLY) 438 or die("$fn: open(): $!\n"); 439 while (<FILE>) { 440 if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) { 441 $func{'Nm'} = $func{'Nm'} || $1; 442 } elsif (m/^\.Ft (\S.*?)\s*$/) { 443 $func{'Ft'} = $func{'Ft'} || $1; 444 } elsif (m/^\.Fn (\S.*?)\s*$/) { 445 $func{'Fn'} = $func{'Fn'} || $1; 446 } 447 } 448 close(FILE); 449 if ($func{'Nm'}) { 450 $FUNCTIONS{$func{'Nm'}} = \%func; 451 } else { 452 warn("No function found\n"); 453 } 454} 455 456sub gensummary($) { 457 my $page = shift; # Which page to produce 458 459 local *FILE; 460 my $upage; 461 my $func; 462 my %xref; 463 464 sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC) 465 or die("$page.3: $!\n"); 466 467 $upage = uc($page); 468 print FILE "$COPYRIGHT 469.Dd $TODAY 470.Dt $upage 3 471.Os 472.Sh NAME 473"; 474 my @funcs = sort(keys(%FUNCTIONS)); 475 while ($func = shift(@funcs)) { 476 print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}"; 477 print FILE " ," 478 if (@funcs); 479 print FILE "\n"; 480 } 481 print FILE ".Nd Pluggable Authentication Modules Library 482.Sh LIBRARY 483.Lb libpam 484.Sh SYNOPSIS\n"; 485 if ($page eq 'pam') { 486 print FILE ".In security/pam_appl.h\n"; 487 } else { 488 print FILE ".In security/openpam.h\n"; 489 } 490 foreach $func (sort(keys(%FUNCTIONS))) { 491 print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n"; 492 print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n"; 493 } 494 while (<STDIN>) { 495 if (m/^\.Xr (\S+)\s*(\d)\s*$/) { 496 $xref{$1} = $2; 497 } 498 print FILE $_; 499 } 500 501 if ($page eq 'pam') { 502 print FILE ".Sh RETURN VALUES 503The following return codes are defined by 504.Aq Pa security/pam_constants.h : 505.Bl -tag -width 18n 506"; 507 foreach (sort(keys(%PAMERR))) { 508 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n"; 509 } 510 print FILE ".El\n"; 511 } 512 print FILE ".Sh SEE ALSO 513"; 514 print FILE ".Xr openpam 3\n" 515 if ($page eq 'pam'); 516 foreach $func (keys(%FUNCTIONS)) { 517 $xref{$func} = 3; 518 } 519 my @refs = sort(keys(%xref)); 520 while ($_ = shift(@refs)) { 521 print FILE ".Xr $_ $xref{$_}"; 522 print FILE " ," 523 if (@refs); 524 print FILE "\n"; 525 } 526 print FILE ".Sh STANDARDS 527.Rs 528.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 529.%D \"June 1997\" 530.Re 531.Sh AUTHORS 532The OpenPAM library and this manual page were developed for the 533FreeBSD Project by ThinkSec AS and Network Associates Laboratories, 534the Security Research Division of Network Associates, Inc. under 535DARPA/SPAWAR contract N66001-01-C-8035 536.Pq Dq CBOSS , 537as part of the DARPA CHATS research program. 538"; 539 close(FILE); 540} 541 542sub usage() { 543 544 print(STDERR "usage: gendoc [-s] source [...]\n"); 545 exit(1); 546} 547 548MAIN:{ 549 my %opts; 550 551 usage() 552 unless (@ARGV && getopts("op", \%opts)); 553 $TODAY = strftime("%B %e, %Y", localtime(time())); 554 $TODAY =~ s,\s+, ,g; 555 if ($opts{'o'} || $opts{'p'}) { 556 foreach my $fn (@ARGV) { 557 readproto($fn); 558 } 559 gensummary('openpam') 560 if ($opts{'o'}); 561 gensummary('pam') 562 if ($opts{'p'}); 563 } else { 564 foreach my $fn (@ARGV) { 565 my $func = parse_source($fn); 566 gendoc($func) 567 if (defined($func)); 568 } 569 } 570 exit(0); 571} 572