3 # Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4 # Copyright (c) 2004-2014 Dag-Erling Smørgrav
7 # This software was developed for the FreeBSD Project by ThinkSec AS and
8 # Network Associates Laboratories, the Security Research Division of
9 # Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 # ("CBOSS"), as part of the DARPA CHATS research program.
12 # Redistribution and use in source and binary forms, with or without
13 # modification, are permitted provided that the following conditions
15 # 1. Redistributions of source code must retain the above copyright
16 # notice, this list of conditions and the following disclaimer.
17 # 2. Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution.
20 # 3. The name of the author may not be used to endorse or promote
21 # products derived from this software without specific prior written
24 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 # $Id: gendoc.pl 910 2017-01-21 12:22:08Z des $
45 use POSIX qw(strftime);
46 use vars qw(%AUTHORS $TODAY %FUNCTIONS %PAMERR);
49 THINKSEC => "developed for the
51 Project by ThinkSec AS and Network Associates Laboratories, the
52 Security Research Division of Network Associates, Inc.\\& under
53 DARPA/SPAWAR contract N66001-01-C-8035
55 as part of the DARPA CHATS research program.
57 The OpenPAM library is maintained by
58 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
59 UIO => "developed for the University of Oslo by
60 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
62 .An Dag-Erling Sm\\(/orgrav Aq Mt des\@des.no .",
66 PAM_SUCCESS => "Success",
67 PAM_OPEN_ERR => "Failed to load module",
68 PAM_SYMBOL_ERR => "Invalid symbol",
69 PAM_SERVICE_ERR => "Error in service module",
70 PAM_SYSTEM_ERR => "System error",
71 PAM_BUF_ERR => "Memory buffer error",
72 PAM_CONV_ERR => "Conversation failure",
73 PAM_PERM_DENIED => "Permission denied",
74 PAM_MAXTRIES => "Maximum number of tries exceeded",
75 PAM_AUTH_ERR => "Authentication error",
76 PAM_NEW_AUTHTOK_REQD => "New authentication token required",
77 PAM_CRED_INSUFFICIENT => "Insufficient credentials",
78 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable",
79 PAM_USER_UNKNOWN => "Unknown user",
80 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials",
81 PAM_CRED_EXPIRED => "User credentials have expired",
82 PAM_CRED_ERR => "Failed to set user credentials",
83 PAM_ACCT_EXPIRED => "User account has expired",
84 PAM_AUTHTOK_EXPIRED => "Password has expired",
85 PAM_SESSION_ERR => "Session failure",
86 PAM_AUTHTOK_ERR => "Authentication token failure",
87 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token",
88 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy",
89 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled",
90 PAM_NO_MODULE_DATA => "Module data not found",
91 PAM_IGNORE => "Ignore this module",
92 PAM_ABORT => "General failure",
93 PAM_TRY_AGAIN => "Try again",
94 PAM_MODULE_UNKNOWN => "Unknown module type",
95 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain",
120 if ($fn !~ m,\.c$,) {
121 warn("$fn: not C source, ignoring\n");
125 open(FILE, "<", "$fn")
126 or die("$fn: open(): $!\n");
127 $source = join('', <FILE>);
131 if ($source =~ m/^ \* NOPARSE\s*$/m);
133 if ($source =~ m/(\$Id:[^\$]+\$)/) {
137 $author = 'THINKSEC';
138 if ($source =~ s/^ \* AUTHOR\s+(\w*)\s*$//m) {
142 if ($source =~ s/^ \* DEPRECATED\s*(\w*)\s*$//m) {
143 $deprecated = $1 // 0;
146 if ($source =~ s/^ \* EXPERIMENTAL\s*$//m) {
151 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
152 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
153 warn("$fn: can't find $func\n");
156 ($descr, $type, $args) = ($1, $2, $3);
157 $descr =~ s,^([A-Z][a-z]),lc($1),e;
158 $descr =~ s,[\.\s]*$,,;
159 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
162 $args =~ s/,\s+/, /gs;
169 if ($type eq "int") {
170 foreach (split("\n", $source)) {
171 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
174 ++$xref{3}->{pam_strerror};
178 # extract names of regular arguments
179 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
180 # extract names of function pointer arguments
181 $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
182 # escape metacharacters (there shouldn't be any, but...)
183 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
184 # separate argument names with |
185 $argnames =~ s/\" \"/|/g;
186 # and surround with ()
187 $argnames =~ s/^\"(.*)\"$/$1/;
188 # $argnames is now a regexp that matches argument names
189 $inliteral = $inlist = $intaglist = 0;
190 foreach (split("\n", $source)) {
192 if (!defined($man)) {
198 last if (m/^ \*\/$/);
202 # paragraph separator
203 if ($inlist || $intaglist) {
204 # either a blank line between list items, or a blank
205 # line after the final list item. The latter case
206 # will be handled further down.
209 if ($man =~ m/\n\.Sh [^\n]+\n$/s) {
210 # a blank line after a section header
213 if ($man ne "" && $man !~ m/\.Pp\n$/s) {
222 if (m/^>(\w+)(\s+\d)?$/) {
223 # "see also" cross-reference
224 my ($page, $sect) = ($1, $2 ? int($2) : 3);
225 ++$xref{$sect}->{$page};
228 if (s/^([A-Z][0-9A-Z -]+)$/.Sh $1/) {
229 if ($1 eq "RETURN VALUES") {
232 $man =~ s/\n\.Pp$/\n/s;
237 # item in bullet list
243 $man .= ".El\n.Pp\n";
247 $man =~ s/\.Pp\n$//s;
248 $man .= ".Bl -bullet\n";
253 } elsif (s/^\s+(\S+):\s*/.It $1/) {
260 $man .= ".El\n.Pp\n";
264 $man =~ s/\.Pp\n$//s;
265 $man .= ".Bl -tag -width 18n\n";
268 s/^\.It [=;]([A-Za-z][0-9A-Za-z_]+)$/.It Dv $1/gs;
271 } elsif (($inlist || $intaglist) && m/^\S/) {
272 # regular text after list
273 $man .= ".El\n.Pp\n";
274 $inlist = $intaglist = 0;
275 } elsif ($inliteral && m/^\S/) {
276 # regular text after literal section
279 } elsif ($inliteral) {
280 # additional text within literal section
283 } elsif ($inlist || $intaglist) {
284 # additional text within list
287 # new literal section
288 $man .= ".Bd -literal\n";
293 s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
294 s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
295 s/\s*=((?:enum|struct|union) \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
296 s/\s*:([a-z][0-9a-z_]+)\b\s*/\n.Va $1\n/gs;
297 s/\s*;([a-z][0-9a-z_]+)\b\s*/\n.Dv $1\n/gs;
298 s/\s*=!([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
299 while (s/\s*=([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
302 s/\s*\"(?=\w)/\n.Do\n/gs;
303 s/\"(?!\w)\s*/\n.Dc\n/gs;
304 s/\s*=([A-Z][0-9A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
305 s/\s*=([A-Z][0-9A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
306 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
310 if ($inlist || $intaglist) {
312 $inlist = $intaglist = 0;
318 $man =~ s/\%/\\&\%/gs;
319 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
320 $man =~ s/\s*$/\n/gm;
323 $man =~ s/\n\n\./\n\./gs;
326 $man = "No description available.";
329 $FUNCTIONS{$func} = {
331 'version' => $version,
338 'errors' => \@errors,
340 'customrv' => $customrv,
341 'deprecated' => $deprecated,
342 'experimental' => $experimental,
344 if ($source =~ m/^ \* NODOC\s*$/m) {
345 $FUNCTIONS{$func}->{nodoc} = 1;
347 if ($source !~ m/^ \* XSSO \d/m) {
348 $FUNCTIONS{$func}->{openpam} = 1;
350 expand_errors($FUNCTIONS{$func});
351 return $FUNCTIONS{$func};
354 sub expand_errors($);
355 sub expand_errors($) {
356 my $func = shift; # Ref to function hash
362 if (defined($$func{recursed})) {
363 warn("$$func{name}(): loop in error spec\n");
366 $$func{recursed} = 1;
368 foreach (@{$$func{errors}}) {
369 if (m/^(PAM_[A-Z_]+)$/) {
370 if (!defined($PAMERR{$1})) {
371 warn("$$func{name}(): unrecognized error: $1\n");
375 } elsif (m/^!(PAM_[A-Z_]+)$/) {
376 # treat negations separately
377 } elsif (m/^=([a-z_]+)$/) {
379 if (!defined($FUNCTIONS{$ref})) {
380 $fn = $$func{source};
381 $fn =~ s/$$func{name}/$ref/;
384 if (!defined($FUNCTIONS{$ref})) {
385 warn("$$func{name}(): reference to unknown $ref()\n");
388 foreach (@{$FUNCTIONS{$ref}->{errors}}) {
392 warn("$$func{name}(): invalid error specification: $_\n");
395 foreach (@{$$func{errors}}) {
396 if (m/^!(PAM_[A-Z_]+)$/) {
400 delete($$func{recursed});
401 $$func{errors} = [ sort(keys(%errors)) ];
404 sub dictionary_order($$) {
407 $a =~ s/[^[:alpha:]]//g;
408 $b =~ s/[^[:alpha:]]//g;
413 my $xref = shift; # References
417 foreach my $sect (sort(keys(%{$xref}))) {
418 foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
419 push(@refs, "$page $sect");
422 while ($_ = shift(@refs)) {
424 (@refs ? " ,\n" : "\n");
430 my $func = shift; # Ref to function hash
436 return if defined($$func{nodoc});
438 $$func{source} =~ m/([^\/]+)$/;
439 $mdoc = ".\\\" Generated from $1 by gendoc.pl\n";
440 if ($$func{version}) {
441 $mdoc .= ".\\\" $$func{version}\n";
444 .Dt " . uc($$func{name}) . " 3
450 if ($func =~ m/^(?:open)?pam_/) {
451 $mdoc .= ".Sh LIBRARY
455 $mdoc .= ".Sh SYNOPSIS
458 if ($$func{args} =~ m/\bFILE \*\b/) {
459 $mdoc .= ".In stdio.h\n";
461 if ($$func{name} =~ m/^(?:open)?pam/) {
462 $mdoc .= ".In security/pam_appl.h
465 if ($$func{name} =~ m/_sm_/) {
466 $mdoc .= ".In security/pam_modules.h\n";
468 if ($$func{name} =~ m/openpam/) {
469 $mdoc .= ".In security/openpam.h\n";
471 $mdoc .= ".Ft \"$$func{type}\"
472 .Fn $$func{name} $$func{args}
475 if (defined($$func{deprecated})) {
476 $mdoc .= ".Bf Sy\n" .
477 "This function is deprecated and may be removed " .
478 "in a future release without further warning.\n";
479 if ($$func{deprecated}) {
480 $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " .
481 "may be used to achieve similar results.\n";
483 $mdoc .= ".Ef\n.Pp\n";
485 if ($$func{experimental}) {
486 $mdoc .= ".Bf Sy\n" .
487 "This function is experimental and may be modified or removed " .
488 "in a future release without prior warning.\n";
489 $mdoc .= ".Ef\n.Pp\n";
491 $mdoc .= "$$func{man}\n";
492 my @errors = @{$$func{errors}};
493 if ($$func{customrv}) {
495 } elsif ($$func{type} eq "int" && @errors) {
496 $mdoc .= ".Sh RETURN VALUES
499 function returns one of the following values:
503 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
506 } elsif ($$func{type} eq "int") {
507 $mdoc .= ".Sh RETURN VALUES
510 function returns 0 on success and -1 on failure.
512 } elsif ($$func{type} =~ m/\*$/) {
513 $mdoc .= ".Sh RETURN VALUES
520 } elsif ($$func{type} ne "void") {
521 warn("$$func{name}(): no error specification\n");
523 $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref});
524 $mdoc .= ".Sh STANDARDS\n";
525 if ($$func{openpam}) {
528 function is an OpenPAM extension.
532 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
537 $mdoc .= ".Sh AUTHORS
540 function and this manual page were\n";
541 $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
542 $fn = "$$func{name}.3";
543 if (open(FILE, ">", $fn)) {
547 warn("$fn: open(): $!\n");
552 my $fn = shift; # File name
557 open(FILE, "<", "$fn")
558 or die("$fn: open(): $!\n");
560 if (m/^\.Nm ((?:(?:open)?pam)_.*?)\s*$/) {
561 $func{Nm} = $func{Nm} || $1;
562 } elsif (m/^\.Ft (\S.*?)\s*$/) {
563 $func{Ft} = $func{Ft} || $1;
564 } elsif (m/^\.Fn (\S.*?)\s*$/) {
565 $func{Fn} = $func{Fn} || $1;
570 $FUNCTIONS{$func{Nm}} = \%func;
572 warn("No function found\n");
577 my $page = shift; # Which page to produce
584 open(FILE, ">", "$page.3")
585 or die("$page.3: $!\n");
589 print FILE ".\\\" Generated by gendoc.pl
595 my @funcs = sort(keys(%FUNCTIONS));
596 while ($func = shift(@funcs)) {
597 print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
602 print FILE ".Nd Pluggable Authentication Modules Library
606 if ($page eq 'pam') {
607 print FILE ".In security/pam_appl.h\n";
609 print FILE ".In security/openpam.h\n";
611 foreach $func (sort(keys(%FUNCTIONS))) {
612 print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
613 print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
616 if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
617 ++$xref{int($2)}->{$1};
622 if ($page eq 'pam') {
623 print FILE ".Sh RETURN VALUES
624 The following return codes are defined by
625 .In security/pam_constants.h :
628 foreach (sort(keys(%PAMERR))) {
629 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
633 print FILE ".Sh SEE ALSO
635 if ($page eq 'pam') {
636 ++$xref{3}->{openpam};
638 foreach $func (keys(%FUNCTIONS)) {
641 print FILE genxref(\%xref);
642 print FILE ".Sh STANDARDS
644 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
648 print FILE ".Sh AUTHORS
649 The OpenPAM library and this manual page were $AUTHORS{THINKSEC}
656 print(STDERR "usage: gendoc [-op] source [...]\n");
664 unless (@ARGV && getopts("op", \%opts));
665 $TODAY = strftime("%B %e, %Y", localtime(time()));
667 if ($opts{o} || $opts{p}) {
668 foreach my $fn (@ARGV) {
671 gensummary('openpam')
676 foreach my $fn (@ARGV) {
677 my $func = parse_source($fn);