1#!/usr/bin/perl -w 2#- 3# Copyright (c) 2002 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#18 $ 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) 2002 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 if (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/gs) { 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 $FUNCTIONS{$func}->{'nolist'} = 1; 278 } 279 if ($source =~ m/^ \* NOLIST\s*$/m) { 280 $FUNCTIONS{$func}->{'nolist'} = 1; 281 } 282 if ($source !~ m/^ \* XSSO \d/m) { 283 $FUNCTIONS{$func}->{'openpam'} = 1; 284 } 285 expand_errors($FUNCTIONS{$func}); 286 return $FUNCTIONS{$func}; 287} 288 289sub expand_errors($); 290sub expand_errors($) { 291 my $func = shift; # Ref to function hash 292 293 my %errors; 294 my $ref; 295 my $fn; 296 297 if (defined($func->{'recursed'})) { 298 warn("$func->{'name'}(): loop in error spec\n"); 299 return qw(); 300 } 301 $func->{'recursed'} = 1; 302 303 foreach (@{$func->{'errors'}}) { 304 if (m/^(PAM_[A-Z_]+)$/) { 305 if (!defined($PAMERR{$1})) { 306 warn("$func->{'name'}(): unrecognized error: $1\n"); 307 next; 308 } 309 $errors{$1} = 1; 310 } elsif (m/^!(PAM_[A-Z_]+)$/) { 311 # treat negations separately 312 } elsif (m/^=([a-z_]+)$/) { 313 $ref = $1; 314 if (!defined($FUNCTIONS{$ref})) { 315 $fn = $func->{'source'}; 316 $fn =~ s/$func->{'name'}/$ref/; 317 parse_source($fn); 318 } 319 if (!defined($FUNCTIONS{$ref})) { 320 warn("$func->{'name'}(): reference to unknown $ref()\n"); 321 next; 322 } 323 foreach (@{$FUNCTIONS{$ref}->{'errors'}}) { 324 $errors{$_} = 1; 325 } 326 } else { 327 warn("$func->{'name'}(): invalid error specification: $_\n"); 328 } 329 } 330 foreach (@{$func->{'errors'}}) { 331 if (m/^!(PAM_[A-Z_]+)$/) { 332 delete($errors{$1}); 333 } 334 } 335 delete($func->{'recursed'}); 336 $func->{'errors'} = [ sort(keys(%errors)) ]; 337} 338 339sub gendoc($) { 340 my $func = shift; # Ref to function hash 341 342 local *FILE; 343 my $mdoc; 344 my $fn; 345 346 return if defined($func->{'nodoc'}); 347 348 $mdoc = "$COPYRIGHT 349.Dd $TODAY 350.Dt " . uc($func->{'name'}) . " 3 351.Os 352.Sh NAME 353.Nm $func->{'name'} 354.Nd $func->{'descr'} 355.Sh LIBRARY 356.Lb libpam 357.Sh SYNOPSIS 358.In security/pam_appl.h 359"; 360 if ($func->{'name'} =~ m/_sm_/) { 361 $mdoc .= ".In security/pam_modules.h\n" 362 } 363 if ($func->{'name'} =~ m/openpam/) { 364 $mdoc .= ".In security/openpam.h\n" 365 } 366 $mdoc .= ".Ft $func->{'type'} 367.Fn $func->{'name'} $func->{'args'} 368.Sh DESCRIPTION 369$func->{'man'} 370"; 371 if ($func->{'type'} eq "int") { 372 $mdoc .= ".Sh RETURN VALUES 373The 374.Nm 375function returns one of the following values: 376.Bl -tag -width 18n 377"; 378 my @errors = @{$func->{'errors'}}; 379 warn("$func->{'name'}(): no error specification\n") 380 unless(@errors); 381 foreach (@errors) { 382 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; 383 } 384 $mdoc .= ".El\n"; 385 } else { 386 if ($func->{'type'} =~ m/\*$/) { 387 $mdoc .= ".Sh RETURN VALUES 388The 389.Nm 390function returns 391.Dv NULL 392on failure. 393"; 394 } 395 } 396 $mdoc .= ".Sh SEE ALSO\n"; 397 my @xref = sort(keys(%{$func->{'xref'}})); 398 while (@xref) { 399 $mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n"); 400 } 401 $mdoc .= ".Sh STANDARDS\n"; 402 if ($func->{'openpam'}) { 403 $mdoc .= "The 404.Nm 405function is an OpenPAM extension. 406"; 407 } else { 408 $mdoc .= ".Rs 409.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 410.%D \"June 1997\" 411.Re 412"; 413 } 414 $mdoc .= ".Sh AUTHORS 415The 416.Nm 417function and this manual page were developed for the FreeBSD Project 418by ThinkSec AS and Network Associates Laboratories, the Security 419Research Division of Network Associates, Inc. under DARPA/SPAWAR 420contract N66001-01-C-8035 421.Pq Dq CBOSS , 422as part of the DARPA CHATS research program. 423"; 424 425 $fn = "$func->{'name'}.3"; 426 if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) { 427 print(FILE $mdoc); 428 close(FILE); 429 } else { 430 warn("$fn: open(): $!\n"); 431 } 432} 433 434sub readproto($) { 435 my $fn = shift; # File name 436 437 local *FILE; 438 my %func; 439 440 sysopen(FILE, $fn, O_RDONLY) 441 or die("$fn: open(): $!\n"); 442 while (<FILE>) { 443 if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) { 444 $func{'Nm'} = $func{'Nm'} || $1; 445 } elsif (m/^\.Ft (\S.*?)\s*$/) { 446 $func{'Ft'} = $func{'Ft'} || $1; 447 } elsif (m/^\.Fn (\S.*?)\s*$/) { 448 $func{'Fn'} = $func{'Fn'} || $1; 449 } 450 } 451 close(FILE); 452 if ($func{'Nm'}) { 453 $FUNCTIONS{$func{'Nm'}} = \%func; 454 } else { 455 warn("No function found\n"); 456 } 457} 458 459sub gensummary($) { 460 my $page = shift; # Which page to produce 461 462 local *FILE; 463 my $upage; 464 my $func; 465 my %xref; 466 467 sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC) 468 or die("$page.3: $!\n"); 469 470 $upage = uc($page); 471 print FILE "$COPYRIGHT 472.Dd $TODAY 473.Dt $upage 3 474.Os 475.Sh NAME 476"; 477 my @funcs = sort(keys(%FUNCTIONS)); 478 while ($func = shift(@funcs)) { 479 print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}"; 480 print FILE " ," 481 if (@funcs); 482 print FILE "\n"; 483 } 484 print FILE ".Nd Pluggable Authentication Modules Library 485.Sh LIBRARY 486.Lb libpam 487.Sh SYNOPSIS\n"; 488 if ($page eq 'pam') { 489 print FILE ".In security/pam_appl.h\n"; 490 } else { 491 print FILE ".In security/openpam.h\n"; 492 } 493 foreach $func (sort(keys(%FUNCTIONS))) { 494 print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n"; 495 print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n"; 496 } 497 while (<STDIN>) { 498 if (m/^\.Xr (\S+)\s*(\d)\s*$/) { 499 $xref{$1} = $2; 500 } 501 print FILE $_; 502 } 503 504 if ($page eq 'pam') { 505 print FILE ".Sh RETURN VALUES 506The following return codes are defined by 507.Aq Pa security/pam_constants.h : 508.Bl -tag -width 18n 509"; 510 foreach (sort(keys(%PAMERR))) { 511 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n"; 512 } 513 print FILE ".El\n"; 514 } 515 print FILE ".Sh SEE ALSO 516"; 517 print FILE ".Xr openpam 3\n" 518 if ($page eq 'pam'); 519 foreach $func (keys(%FUNCTIONS)) { 520 $xref{$func} = 3; 521 } 522 my @refs = sort(keys(%xref)); 523 while ($_ = shift(@refs)) { 524 print FILE ".Xr $_ $xref{$_}"; 525 print FILE " ," 526 if (@refs); 527 print FILE "\n"; 528 } 529 print FILE ".Sh STANDARDS 530.Rs 531.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 532.%D \"June 1997\" 533.Re 534.Sh AUTHORS 535The OpenPAM library and this manual page were developed for the 536FreeBSD Project by ThinkSec AS and Network Associates Laboratories, 537the Security Research Division of Network Associates, Inc. under 538DARPA/SPAWAR contract N66001-01-C-8035 539.Pq Dq CBOSS , 540as part of the DARPA CHATS research program. 541"; 542 close(FILE); 543} 544 545sub usage() { 546 547 print(STDERR "usage: gendoc [-s] source [...]\n"); 548 exit(1); 549} 550 551MAIN:{ 552 my %opts; 553 554 usage() 555 unless (@ARGV && getopts("op", \%opts)); 556 $TODAY = strftime("%B %e, %Y", localtime(time())); 557 $TODAY =~ s,\s+, ,g; 558 if ($opts{'o'} || $opts{'p'}) { 559 foreach my $fn (@ARGV) { 560 readproto($fn); 561 } 562 gensummary('openpam') 563 if ($opts{'o'}); 564 gensummary('pam') 565 if ($opts{'p'}); 566 } else { 567 foreach my $fn (@ARGV) { 568 my $func = parse_source($fn); 569 gendoc($func) 570 if (defined($func)); 571 } 572 } 573 exit(0); 574} 575