xref: /freebsd/contrib/openpam/misc/gendoc.pl (revision 390e8cc2974df1888369c06339ef8e0e92b312b6)
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