Import of tac_plus.v8.tar.gz: 173206 bytes, md5:
[tac_plus.git] / do_author.c
1 /* 
2    Copyright (c) 1995-1998 by Cisco systems, Inc.
3
4    Permission to use, copy, modify, and distribute this software for
5    any purpose and without fee is hereby granted, provided that this
6    copyright and permission notice appear on all copies of the
7    software and supporting documentation, the name of Cisco Systems,
8    Inc. not be used in advertising or publicity pertaining to
9    distribution of the program without specific prior permission, and
10    notice be given in supporting documentation that modification,
11    copying and distribution is by permission of Cisco Systems, Inc.
12
13    Cisco Systems, Inc. makes no representations about the suitability
14    of this software for any purpose.  THIS SOFTWARE IS PROVIDED ``AS
15    IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
16    WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17    FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 #include "tac_plus.h"
21 #include "regexp.h"
22
23 static int get_nas_svc();
24 static int authorize_cmd();
25 static int authorize_exec();
26 static int authorize_svc();
27 static void post_authorization();
28 static int pre_authorization();
29
30 /* Return 0 is data->status is valid */
31 int
32 do_author(data)
33 struct author_data *data;
34 {
35     char *username = data->id->username;
36     int status;
37     int svc;
38     char *cmd, *protocol, *svcname;
39 #ifdef USE_PAM
40     char *pam_service= NULL;
41 #endif
42     protocol = NULL;
43
44     data->status = AUTHOR_STATUS_FAIL;  /* for safety */
45
46     data->output_args = NULL;
47     data->num_out_args = 0;
48
49     /* If this user doesn't exist in our configs, do the default */
50
51     if (!cfg_user_exists(username) && !cfg_user_exists(DEFAULT_USERNAME)) {
52         
53         if (cfg_no_user_permitted()) {
54             if (debug & DEBUG_AUTHOR_FLAG)
55                 report(LOG_DEBUG, 
56                        "user '%s' or '%s' not found, permitted by default", 
57                        username, DEFAULT_USERNAME);
58
59             data->status = AUTHOR_STATUS_PASS_ADD;
60             data->output_args = NULL;
61             data->num_out_args = 0;
62             return (0);
63         }
64
65         if (debug & DEBUG_AUTHOR_FLAG)
66             report(LOG_DEBUG, 
67                    "user '%s' or '%s' not found, denied by default", 
68                    username, DEFAULT_USERNAME);
69         data->status = AUTHOR_STATUS_FAIL;
70         return (0);
71     }
72
73     if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
74         if (debug & DEBUG_AUTHOR_FLAG) {
75             report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'", 
76                    DEFAULT_USERNAME, username);
77         }
78         username = DEFAULT_USERNAME;
79     }
80
81     /* See if there's a program defined which will do authorization for us */
82     if (pre_authorization(username, data)) 
83         return(0);
84     
85     /* 
86      * Decide what kind of authorization request this is. Currently
87      * one of: exec, cmd, slip, arap, ppp or <string>
88      * 
89      * If it's a command typed to the exec, return its text in cmd.
90      * 
91      * If it's a ppp request, return the protocol name in protocol.
92      */
93
94     svc = get_nas_svc(data, &cmd, &protocol, &svcname);
95
96     if (!svc) {
97         /* if we can't identify the service in the request it's an error */
98         data->status = AUTHOR_STATUS_ERROR;
99         data->admin_msg =
100         tac_strdup("No identifiable service/protocol in authorization request");
101         if (debug & DEBUG_AUTHOR_FLAG) {
102             report(LOG_DEBUG, "user %s %s", username, data->admin_msg);
103         }
104         return (0);
105     }
106
107     if (debug & DEBUG_AUTHOR_FLAG)
108         report(LOG_DEBUG, "user '%s' found", username);
109
110 #ifdef MAXSESS
111     /* Never permit if they're going over their session limit */
112     switch (svc) {
113     case N_svc_arap:
114     case N_svc_ppp:
115     case N_svc_slip:
116     case N_svc_exec:
117 /*    case N_svc: */
118         if (maxsess_check_count(username, data)) {
119             return(0);
120         }
121
122     default:
123         break;
124     }
125 #endif /* MAXSESS */
126
127 #ifdef USE_PAM
128         /* Check  PAM Authorization */
129      switch (svc) {
130     case N_svc_ppp:
131     case N_svc_exec:
132 if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
133
134         if (debug & DEBUG_AUTHOR_FLAG)
135                 report(LOG_DEBUG, "PAM Authorization begin for user %s",data->id->username);
136         if(tac_pam_authorization(data->id->username,data,pam_service))
137         {
138                 if (debug & DEBUG_AUTHOR_FLAG)
139                         report(LOG_DEBUG, "PAM Authorization Fail");
140                 return(0);
141         }
142 } /* Pam_service */
143     default:
144         break;
145     }
146 #endif 
147
148     switch(svc) {
149     default:
150         report(LOG_ERR, "%s: Bad service type %d", session.peer, svc);
151         data->status = AUTHOR_STATUS_FAIL;
152         return(0);
153
154     case N_svc_cmd:
155         /* A command authorisation request */
156         status = authorize_cmd(username, cmd, data);
157         break;
158  
159     case N_svc_exec:
160         if (authorize_exec(username, data)) 
161             return(0);
162         /* FALLTHRU */
163
164     case N_svc_arap:
165     case N_svc_ppp:
166     case N_svc_slip:
167         status = authorize_svc(username, svc, protocol, NULL, data);
168         break;
169
170     case N_svc:
171         status = authorize_svc(username, svc, protocol, svcname, data);
172         break;
173     }
174
175     post_authorization(username, data);
176     return(status);    
177 }
178
179 /* If an before-authorization program has been specified, call it. 
180
181    A return value of 1 means no further authorization is required
182 */
183
184 static int
185 pre_authorization(username, data) 
186 char *username;
187 struct author_data *data;
188 {
189     int status;
190     char **out_args;
191     int out_cnt, i;
192     char *cmd;
193     char error_str[255];
194     int error_len = 255;
195
196     out_cnt = 0;
197     out_args = NULL;
198
199     /* If a before-authorization program exists, call it to see how to
200        proceed */
201
202     cmd = cfg_get_pvalue(username, TAC_IS_USER, 
203                          S_before, TAC_PLUS_RECURSE);
204     if (!cmd) 
205         return(0);
206
207     if (debug & DEBUG_AUTHOR_FLAG)
208         report(LOG_DEBUG, "Before authorization call: %s", cmd);
209
210     status = call_pre_process(cmd, data, &out_args, &out_cnt, error_str,
211                               error_len);
212
213     switch (status) {
214     default:
215         if (debug & DEBUG_AUTHOR_FLAG)
216             report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)", 
217                    cmd, status);
218
219         data->status = AUTHOR_STATUS_ERROR;
220         data->admin_msg = 
221             tac_strdup("Illegal return status from pre-authorization command");
222         data->msg = tac_strdup(error_str);
223         data->num_out_args = 0;
224         data->output_args = NULL;
225         /* throw away out_args */
226         for(i=0; i < out_cnt; i++) {
227             free(out_args[i]);
228         }
229         if (out_args) {
230             free(out_args);
231         }
232         return(1);
233
234     case 0: /* Permit */
235         if (debug & DEBUG_AUTHOR_FLAG)
236             report(LOG_DEBUG, "cmd %s returns 0 (unconditional permit)", cmd);
237
238         data->status = AUTHOR_STATUS_PASS_ADD;
239         data->num_out_args = 0;
240         data->output_args = NULL;
241
242         /* throw away out_args */
243         for(i=0; i < out_cnt; i++) {
244             free(out_args[i]);
245         }
246         if (out_args) {
247             free(out_args);
248         }
249         return(1);
250             
251     case 1: /* Deny */
252         if (debug & DEBUG_AUTHOR_FLAG)
253             report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)", 
254                    cmd, status);
255
256         data->status = AUTHOR_STATUS_FAIL;
257         data->msg = tac_strdup(error_str);
258         data->num_out_args = 0;
259         data->output_args = NULL;
260
261         /* throw away out_args */
262         for(i=0; i < out_cnt; i++) {
263             free(out_args[i]);
264         }
265         if (out_args) {
266             free(out_args);
267         }
268         return(1);
269
270     case 2: /* Use replacement AV pairs from program as final result */
271         if (debug & DEBUG_AUTHOR_FLAG) {
272             report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)", 
273                    cmd, status);
274             for(i=0; i < out_cnt; i++)
275                 report(LOG_DEBUG, "%s", out_args[i]); 
276         }
277
278         /* and install the new set of AV pairs as output */
279         data->output_args = out_args;
280         data->num_out_args = out_cnt;
281         data->status = AUTHOR_STATUS_PASS_REPL;
282         return(1); /* no more processing required */
283
284     case 3: /* deny, but return attributes and server-msg to NAS */
285         if (debug & DEBUG_AUTHOR_FLAG) {
286             report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)", 
287                    cmd, status);
288             for(i=0; i < out_cnt; i++)
289                 report(LOG_DEBUG, "%s", out_args[i]); 
290         }
291
292         /* and install the new set of AV pairs as output */
293         data->output_args = out_args;
294         data->num_out_args = out_cnt;
295         data->msg = tac_strdup(error_str);
296         data->status = AUTHOR_STATUS_FAIL;
297         return(1); /* no more processing required */
298     }
299 }
300
301 /* If an after-authorization program has been specified, call it. It
302    can rewrite the output arguments in the authorization data, or
303    change the authorization status by calling an external program.
304 */
305
306 static void
307 post_authorization(username, data) 
308     char *username;
309     struct author_data *data;
310 {
311     char **out_args;
312     int out_cnt, i;
313     int status;
314     char *after = cfg_get_pvalue(username, TAC_IS_USER, 
315                                 S_after, TAC_PLUS_RECURSE);
316     if (!after) 
317         return;
318
319     if (debug & DEBUG_AUTHOR_FLAG)
320         report(LOG_DEBUG, "After authorization call: %s", after);
321
322     status = call_post_process(after, data, &out_args, &out_cnt);
323
324     if (status != 2) {
325         /* throw away out_args */
326         for(i=0; i < out_cnt; i++) {
327             free(out_args[i]);
328         }
329         free(out_args);
330     }
331
332     switch (status) {
333     default:
334         if (debug & DEBUG_AUTHOR_FLAG)
335             report(LOG_DEBUG, 
336                    "cmd %s returns %d (Error)", after, status);
337
338         data->status = AUTHOR_STATUS_ERROR;
339         return;
340
341     case 0:                             /* Permit */
342         if (debug & DEBUG_AUTHOR_FLAG)
343             report(LOG_DEBUG, "cmd %s returns 0 (no change)", after);
344         return;
345             
346     case 1:                             /* Deny */
347         if (debug & DEBUG_AUTHOR_FLAG)
348             report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)", 
349                    after, status);
350
351         data->status = AUTHOR_STATUS_FAIL;
352         return;
353
354     case 2:
355         /* Use replacement AV pairs from program */
356         if (debug & DEBUG_AUTHOR_FLAG)
357             report(LOG_DEBUG, "cmd %s returns 2 (replace & continue)", 
358                    after);
359
360         /* Free any existing AV output pairs */
361         if (data->num_out_args) {
362             for(i=0; i < data->num_out_args; i++) {
363                 free(data->output_args[i]);
364             }
365             free(data->output_args);
366             data->output_args = NULL;
367         }
368
369         if (debug & DEBUG_AUTHOR_FLAG) {
370             report(LOG_DEBUG, "status is now AUTHOR_STATUS_PASS_REPL");
371         }
372
373         data->status = AUTHOR_STATUS_PASS_REPL;
374         data->output_args = out_args;
375         data->num_out_args = out_cnt;
376         return;
377     }
378 }
379
380
381 /* Return a pointer to the value part of an attr=value string */
382 static char *
383 value(s)
384 char *s;
385 {
386     while (*s && *s != '=' && *s != '*')
387         s++;
388     if (*s)
389         return (++s);
390     return (NULL);
391 }
392
393 /* Reassemble the command arguments as typed by the user, out of the
394    array of args we received. Return "" if there are no arguments */
395
396 static char *
397 assemble_args(data)
398 struct author_data *data;
399 {
400     char *buf;
401     int i;
402     char *nas_arg, *v;
403     int len;
404
405     len = 0;
406     for (i = 0; i < data->num_in_args; i++) {
407         nas_arg = data->input_args[i];
408         if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg"))==0)
409             len += strlen(value(nas_arg)) + 1;
410     }
411
412     if (len <= 0) {
413         return(tac_strdup(""));
414     }
415
416     buf = tac_malloc(len);
417     buf[0] = '\0';
418
419     for (i = 0; i < data->num_in_args; i++) {
420         nas_arg = data->input_args[i];
421         if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg")))
422             continue;
423
424         v = value(nas_arg);
425         if (!v) {
426             free(buf);
427             return (NULL);
428         }
429         strcat(buf, v);
430         if (i < (data->num_in_args - 1))
431             strcat(buf, " ");
432     }
433     return (buf);
434 }
435
436
437 /* See if an exec is authorized. Either the user has explicitly
438    authorized the exec, or she has authorized some commands (which
439    implicitly authorizes an exec), or the default is permit.
440
441    If she has explicitly authorized an exec, we need to process its
442    attribute=value pairs. We indicate this by returning zero to the
443    caller.
444
445    Otherwise, we return 1, indicating no further processing is
446    required for this request. */
447
448 static int
449 authorize_exec(user, data)
450 char *user;
451 struct author_data *data;
452 {
453     NODE *svc;
454
455     if (debug & DEBUG_AUTHOR_FLAG) 
456         report(LOG_DEBUG, "exec authorization request for %s", user);
457
458     /* Is an exec explicitly configured? If so, return 0 so we know to
459        process its attributes */
460
461     svc = cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE);
462     if (svc) {
463         if (debug & DEBUG_AUTHOR_FLAG) 
464             report(LOG_DEBUG, "exec is explicitly permitted by line %d",
465                    svc->line);
466         return (0);
467     }
468
469     /* No exec is configured. Are any commands configured? */
470     svc = cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE);
471     if (svc) {
472         if (debug & DEBUG_AUTHOR_FLAG) 
473             report(LOG_DEBUG, "exec permitted because commands are configured");
474
475         data->status = AUTHOR_STATUS_PASS_ADD;
476         data->output_args = NULL;
477         data->num_out_args = 0;
478         return (1);
479     }
480
481     /* No exec or commands configured. What's the default? */
482     if (cfg_user_svc_default_is_permit(user)) {
483
484         if (debug & DEBUG_AUTHOR_FLAG) 
485             report(LOG_DEBUG, "exec permitted by default");
486
487         data->status = AUTHOR_STATUS_PASS_ADD;
488         data->output_args = NULL;
489         data->num_out_args = 0;
490         return (1);
491     }
492
493     if (debug & DEBUG_AUTHOR_FLAG) 
494         report(LOG_DEBUG, "exec denied by default");
495
496     data->status = AUTHOR_STATUS_FAIL;
497     data->num_out_args = 0;
498     return(1);
499 }
500
501 /* Is an exec command authorized per our database(s)?  
502    Return 0 if status is valid */
503
504 static int
505 authorize_cmd(user, cmd, data)
506 char *user, *cmd;
507 struct author_data *data;
508 {
509     NODE *node;
510     char *args;
511     int match;
512
513     args = assemble_args(data);
514
515     if (!cmd) {
516         data->status = AUTHOR_STATUS_ERROR;
517         data->admin_msg = tac_strdup("No command found");
518         report(LOG_ERR, "%s: %s %s", session.peer, cmd, data->admin_msg);
519         data->num_out_args = 0;
520         return (0);
521     }
522
523     node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE);
524
525     /* The command does not exist. Do the default */
526     if (!node) {
527
528         if (cfg_user_svc_default_is_permit(user)) {
529
530             if (debug & DEBUG_AUTHOR_FLAG) 
531                 report(LOG_DEBUG, "cmd %s does not exist, permitted by default",
532                        cmd);
533             
534             data->status = AUTHOR_STATUS_PASS_ADD;
535             data->num_out_args = 0;
536             if (args)
537                 free(args);
538             return(0);
539         }
540
541         if (debug & DEBUG_AUTHOR_FLAG) 
542             report(LOG_DEBUG, "cmd %s does not exist, denied by default",
543                    cmd);
544
545         data->status = AUTHOR_STATUS_FAIL;
546         data->num_out_args = 0;
547         if (args)
548             free(args);
549         return(0);
550     }
551
552     /* The command exists. The default if nothing matches is DENY */
553     data->status = AUTHOR_STATUS_FAIL;
554     data->num_out_args = 0;
555
556     for (node=node->value1; node && args; node = node->next) {
557         match = regexec((regexp *) node->value1, args);
558
559         if (debug & DEBUG_AUTHOR_FLAG) {
560             report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
561                    node->line, cmd,
562                    node->type == N_permit ? "permit" : "deny",
563                    node->value, args, (match ? "" : "no "));
564         }
565
566         if (!match)
567             continue;
568
569         switch (node->type) {
570         case N_permit:
571             if (debug & DEBUG_AUTHOR_FLAG) {
572                 report(LOG_DEBUG, "%s %s permitted by line %d", 
573                        cmd, args, node->line);
574             }
575             data->status = AUTHOR_STATUS_PASS_ADD;
576             data->num_out_args = 0;
577             break;
578         case N_deny:
579             if (debug & DEBUG_AUTHOR_FLAG) {
580                 report(LOG_DEBUG, "%s %s denied by line %d", 
581                        cmd, args, node->line);
582             }
583             data->status = AUTHOR_STATUS_FAIL;
584             data->num_out_args = 0;
585             break;
586         default:
587             data->status = AUTHOR_STATUS_ERROR;
588             data->admin_msg = tac_strdup("Server error illegal configuration node");
589             report(LOG_ERR, "%s: %s %s %s", 
590                    session.peer, cmd, args, data->admin_msg);
591             break;
592         }
593         if (args)
594             free(args);
595         args = NULL;
596         return (0);
597     }
598     if (args)
599         free(args);
600     return (0);
601 }
602
603 static int
604 is_separator(ch)
605 char ch;
606 {
607     return (ch == '=' || ch == '*');
608 }
609
610 /* check an attr=value pair for well-formedness */
611 static int
612 arg_ok(arg)
613 char *arg;
614 {
615     char *p = arg;
616
617     /* It must contain an attribute */
618     if (!*p)
619         return(0);
620
621     for(p=arg; *p; p++) {
622         if (is_separator(*p)) {
623             if (p == arg) /* no attribute */
624                 return(0);
625             return(1);
626         }
627     }
628     /* no separator */
629     return(0);
630 }
631
632
633 /* return 1 if attrs match, 0 otherwise */
634 static int
635 match_attrs(nas_arg, server_arg)
636 char *nas_arg, *server_arg;
637 {
638     while (*nas_arg && *server_arg) {
639         if (is_separator(*nas_arg) && is_separator(*server_arg)) {
640             return (1);
641         }
642         if (*nas_arg != *server_arg)
643             return (0);
644         nas_arg++;
645         server_arg++;
646     }
647     return (0);
648 }
649
650 /* return 1 if values match, 0 otherwise */
651 static int
652 match_values(nas_arg, server_arg)
653 char *nas_arg, *server_arg;
654 {
655     while (*nas_arg && 
656            *server_arg &&
657            !is_separator(*nas_arg)) {
658         nas_arg++;
659         server_arg++;
660     }
661
662     if (!*nas_arg)
663         return(0);
664
665     /* skip separator */
666     nas_arg++;
667     if (*server_arg)
668         server_arg++;
669     
670     /* compare values */
671     return(STREQ(nas_arg, server_arg));
672 }
673
674 /* Return 1 if arg is mandatory, 0 otherwise */
675 static int
676 mandatory(arg)
677 char *arg;
678 {
679     char *p = arg;
680
681     while (*p && !is_separator(*p))
682         p++;
683
684     /* if we're not at the end, this must be the separator */
685     if (*p && !is_separator(*p)) {
686         report(LOG_ERR, "%s: Error on arg %s cannot find separator", 
687                session.peer, arg);
688         return (0);
689     }
690     return (*p == '=');
691 }
692
693 static int
694 optional(arg)
695 char *arg;
696 {
697     return (!mandatory(arg));
698 }
699
700 /* PPP-LCP requests are a special case. If they are not explicitly
701    configured, but there are other ppp services explicitly configured,
702    we admit (return 0) any PPP-LCP request */
703
704 static int 
705 ppp_lcp_allowed(svc, protocol, user)
706     int svc;
707     char *user, *protocol;
708 {
709     /* This is not a ppp/lcp request. Just Say No */
710     if (!(svc == N_svc_ppp &&
711           protocol &&
712           STREQ(protocol, "lcp")))
713         return(0);
714
715     /* It is an LCP request. Are there PPP services configured */
716     if (cfg_ppp_is_configured(user, TAC_PLUS_RECURSE)) {
717         if (debug & DEBUG_AUTHOR_FLAG) {
718             report(LOG_DEBUG, 
719                    "ppp/lcp request permitted (ppp is configured for %s)",
720                    user);
721         }
722         return(1);
723     }
724     
725     /* It is an LCP request but no PPP services are configured */
726     if (debug & DEBUG_AUTHOR_FLAG) {
727         report(LOG_DEBUG, "ppp/lcp request denied (ppp not configured for %s)",
728                user);
729     }
730     return(0);
731 }
732
733 /* Return 0 means data->status is valid */
734 static int
735 authorize_svc(user, svc, protocol, svcname, data)
736     char *user;
737     int svc;
738     char *svcname;
739     struct author_data *data;
740     char *protocol; /* valid only if svc == ppp */
741 {
742     int max_args;
743     char **out_args, **outp;
744     char *nas_arg, *cfg_arg;
745     int i, j;
746     char **cfg_args;
747     char **cfg_argp;
748     int deny_by_default;
749     NODE *node;
750
751     int replaced = 0;
752     int added = 0;
753     int cfg_cnt;
754
755     /* Does this service exist? */
756     node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE);
757
758     if (!node) {
759         /* Service not found. If the default is permit, or this is an
760            PPP/LCP request and other ppp services are configured,
761            we'll allow it. */
762
763         if (cfg_user_svc_default_is_permit(user)) {
764
765             if (debug & DEBUG_AUTHOR_FLAG)
766                 report(LOG_DEBUG, 
767                "svc=%s protocol=%s svcname=%s not found, permitted by default", 
768                        cfg_nodestring(svc), 
769                        protocol ? protocol : "",
770                        svcname ? svcname : "");
771
772             data->status = AUTHOR_STATUS_PASS_ADD;
773             data->num_out_args = 0;
774             data->output_args = NULL;
775             return(0);
776         }
777
778         if (ppp_lcp_allowed(svc, protocol, user)) {
779             data->status = AUTHOR_STATUS_PASS_ADD;
780             data->num_out_args = 0;
781             data->output_args = NULL;
782             return(0);
783         }
784
785         if (debug & DEBUG_AUTHOR_FLAG)
786             report(LOG_DEBUG, "svc=%s protocol=%s not found, denied by default",
787                    cfg_nodestring(svc), protocol ? protocol : "");
788
789         data->status = AUTHOR_STATUS_FAIL;
790         data->num_out_args = 0;
791         data->output_args = NULL;
792         return(0);
793     }
794     
795     /* Get server args configured in the config file. */
796     cfg_args = cfg_get_svc_attrs(node, &deny_by_default);
797
798     /* Check the nas args for well-formedness */
799     for (i = 0; i < data->num_in_args; i++) {
800         if (!arg_ok(data->input_args[i])) {
801             char buf[MAX_INPUT_LINE_LEN+50];
802             sprintf(buf, "Illegal arg %s from NAS", data->input_args[i]);
803             data->status = AUTHOR_STATUS_ERROR;
804             data->admin_msg = tac_strdup(buf);
805             report(LOG_ERR, "%s: Error %s", session.peer, buf);
806
807             /* free any server arguments */
808             for(cfg_argp = cfg_args; cfg_args && *cfg_argp; cfg_argp++)
809                 free(*cfg_argp);
810             free(cfg_args);
811             return (0);
812         }
813     }
814
815     /* How many configured AV pairs are there ? */
816     for (cfg_cnt = 0; cfg_args && cfg_args[cfg_cnt];)
817         cfg_cnt++;
818
819     /* Allocate space for in + out args */
820     max_args = cfg_cnt + data->num_in_args;
821     out_args = (char **) tac_malloc((max_args + 1) * sizeof(char *));
822     outp = out_args;
823     data->num_out_args = 0;
824
825     bzero(out_args, (max_args + 1) * sizeof(char *));
826
827     for (i = 0; i < data->num_in_args; i++) {
828         nas_arg = data->input_args[i];
829
830         /* always pass these pairs through unchanged */
831         if (match_attrs(nas_arg, "service=") ||
832             match_attrs(nas_arg, "protocol=") ||
833             match_attrs(nas_arg, "cmd=")) {
834
835             if (debug & DEBUG_AUTHOR_FLAG) {
836                 report(LOG_DEBUG, "nas:%s (passed thru)", nas_arg);
837             }
838             *outp++ = tac_strdup(nas_arg);
839             data->num_out_args++;
840             continue;
841         }
842
843         /* NAS AV pair is mandatory */
844         if (mandatory(nas_arg)) {
845
846             /* a). look for an exact attribute,value match in the daemon's
847                mandatory list. If found, add the AV pair to the output */
848
849             for (j = 0; j < cfg_cnt; j++) {
850                 cfg_arg = cfg_args[j];
851                 if (optional(cfg_arg))
852                     continue;
853
854                 if (STREQ(nas_arg, cfg_arg)) {
855                     if (debug & DEBUG_AUTHOR_FLAG) {
856                         report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (a)",
857                                nas_arg, cfg_arg, nas_arg);
858                     }
859                     *outp++ = tac_strdup(nas_arg);
860                     data->num_out_args++;
861                     goto next_nas_arg;
862                 }
863             }
864
865             /* b). If an exact match doesn't exist, look in the
866                daemon's optional list for the first attribute
867                match. If found, add the NAS AV pair to the output */
868
869             for (j = 0; j < cfg_cnt; j++) {
870                 cfg_arg = cfg_args[j];
871                 if (mandatory(cfg_arg))
872                     continue;
873
874                 if (match_attrs(nas_arg, cfg_arg)) {
875                     if (debug & DEBUG_AUTHOR_FLAG) {
876                         report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (b)",
877                                nas_arg, cfg_arg, nas_arg);
878                     }
879                     *outp++ = tac_strdup(nas_arg);
880                     data->num_out_args++;
881                     goto next_nas_arg;
882                 }
883             }
884
885             /* c). If no attribute match exists, deny the command if the
886                default is to deny */
887
888             if (deny_by_default) {
889                 data->status = AUTHOR_STATUS_FAIL;
890                 if (debug & DEBUG_AUTHOR_FLAG) {
891                     report(LOG_DEBUG, "nas:%s svr:absent, default=deny -> denied (c)",
892                            nas_arg);
893                 }
894                 if (out_args) {
895                     for (i = 0; i < data->num_out_args; i++)
896                         free(out_args[i]);
897                     free(out_args);
898                 }
899                 
900                 data->num_out_args = 0;
901                 data->output_args = NULL;
902                 
903                 /* free the server arguments */
904                 for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
905                     free(*cfg_argp);
906                 free(cfg_args);
907                 return (0);
908             }
909
910             /* d). If the default is permit, add the NAS AV pair to
911                the output */
912
913             if (debug & DEBUG_AUTHOR_FLAG) {
914                 report(LOG_DEBUG, 
915                        "nas:%s, svr:absent, default=permit -> add %s (d)",
916                        nas_arg, nas_arg);
917             }
918             *outp++ = tac_strdup(nas_arg);
919             data->num_out_args++;
920             goto next_nas_arg;
921
922         } else {
923
924             /* NAS AV pair is Optional */
925
926             /* e). look for an exact attribute,value match in the mandatory
927                list. If found, add DAEMON's AV pair to output */
928
929             for (j = 0; j < cfg_cnt; j++) {
930                 cfg_arg = cfg_args[j];
931                 if (optional(cfg_arg))
932                     continue;
933
934                 if (match_attrs(nas_arg, cfg_arg) &&
935                     match_values(nas_arg, cfg_arg)) {
936
937                     if (debug & DEBUG_AUTHOR_FLAG) {
938                         report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (e)",
939                                nas_arg, cfg_arg, cfg_arg);
940                     }
941                     *outp++ = tac_strdup(cfg_arg);
942                     data->num_out_args++;
943                     replaced++;
944                     goto next_nas_arg;
945                 }
946             }
947
948             /* f). If not found, look for the first attribute match in
949                the mandatory list. If found, add DAEMONS's AV pair to
950                output */
951             
952             for (j = 0; j < cfg_cnt; j++) {
953                 cfg_arg = cfg_args[j];
954                 if (optional(cfg_arg))
955                     continue;
956
957                 if (match_attrs(nas_arg, cfg_arg)) {
958                     if (debug & DEBUG_AUTHOR_FLAG) {
959                         report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (f)",
960                                nas_arg, cfg_arg, cfg_arg);
961                     }
962                     *outp++ = tac_strdup(cfg_arg);
963                     data->num_out_args++;
964                     replaced++;
965                     goto next_nas_arg;
966                 }
967             }
968
969             /* g). If no mandatory match exists, look for an exact
970                attribute,value pair match among the daemon's optional AV
971                pairs. If found add the DAEMON's matching AV pair to the
972                output.
973                */
974
975             for (j = 0; j < cfg_cnt; j++) {
976                 cfg_arg = cfg_args[j];
977                 if (!optional(cfg_arg))
978                     continue;
979                 
980                 if (match_attrs(nas_arg, cfg_arg) && 
981                     match_values(nas_arg, cfg_arg)) {
982                     if (debug & DEBUG_AUTHOR_FLAG) {
983                         report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (g)",
984                                nas_arg, cfg_arg, cfg_arg);
985                     }
986                     *outp++ = tac_strdup(cfg_arg);
987                     data->num_out_args++;
988                     replaced++;
989                     goto next_nas_arg;
990                 }
991             }
992
993             /* h). If no exact match exists, locate the first
994                attribute match among the daemon's optional AV
995                pairs. If found add the DAEMON's matching AV pair to
996                the output */
997
998             for (j = 0; j < cfg_cnt; j++) {
999                 cfg_arg = cfg_args[j];
1000                 if (!optional(cfg_arg))
1001                     continue;
1002                 
1003                 if (match_attrs(nas_arg, cfg_arg)) {
1004                     if (debug & DEBUG_AUTHOR_FLAG) {
1005                         report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (h)",
1006                                nas_arg, cfg_arg, cfg_arg);
1007                     }
1008                     *outp++ = tac_strdup(cfg_arg);
1009                     data->num_out_args++;
1010                     replaced++;
1011                     goto next_nas_arg;
1012                 }
1013             }
1014
1015
1016             /* i). If no match is found, delete the AV pair if default is
1017                deny */
1018
1019             if (deny_by_default) {
1020                 if (debug & DEBUG_AUTHOR_FLAG) {
1021                     report(LOG_DEBUG, "nas:%s svr:absent/deny -> delete %s (i)",
1022                            nas_arg, nas_arg);
1023                 }
1024                 replaced++;
1025                 goto next_nas_arg;
1026             }
1027
1028             /* j). If the default is permit add the NAS AV pair to the output */
1029
1030             if (debug & DEBUG_AUTHOR_FLAG) {
1031                 report(LOG_DEBUG, "nas:%s svr:absent/permit -> add %s (j)",
1032                        nas_arg, nas_arg);
1033             }
1034             *outp++ = tac_strdup(nas_arg);
1035             data->num_out_args++;
1036             goto next_nas_arg;
1037         }
1038     next_nas_arg:;
1039
1040     }
1041
1042     
1043     /* k). After all AV pairs have been processed, for each mandatory
1044        DAEMON AV pair, if there is no attribute match already in the
1045        output list, add the AV pair (add only one AV pair for each
1046        mandatory attribute) */
1047
1048     for (i = 0; i < cfg_cnt; i++) {
1049         cfg_arg = cfg_args[i];
1050
1051         if (!mandatory(cfg_arg))
1052             continue;   
1053
1054         for (j = 0; j < data->num_out_args; j++) {
1055             char *output_arg = out_args[j];
1056
1057             if (match_attrs(cfg_arg, output_arg)) {
1058                 goto next_cfg_arg;
1059             }
1060         }
1061
1062         /* Attr is required by daemon but not present in
1063            output. Add it */
1064
1065         if (debug & DEBUG_AUTHOR_FLAG) {
1066             report(LOG_DEBUG, "nas:absent, server:%s -> add %s (k)",
1067                    cfg_arg, cfg_arg);
1068         }
1069         added++;
1070         *outp++ = tac_strdup(cfg_arg);
1071         data->num_out_args++;
1072
1073     next_cfg_arg:;
1074         
1075     }
1076        
1077     /* If we replaced or deleted some pairs we must return the entire
1078        list we've constructed */
1079
1080     if (replaced) {
1081         if (debug & DEBUG_AUTHOR_FLAG) {
1082             report(LOG_DEBUG, "replaced %d args", replaced);
1083         }
1084         data->status = AUTHOR_STATUS_PASS_REPL;
1085         data->output_args = out_args;
1086
1087         /* free the server arguments */
1088         for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
1089             free(*cfg_argp);
1090         free(cfg_args);
1091
1092         return (0);
1093     }
1094
1095     /* We added something not on the original nas list, but didn't
1096        replace or delete anything. We should return only the
1097        additions */
1098
1099     if (added) {
1100
1101         if (debug & DEBUG_AUTHOR_FLAG) 
1102             report(LOG_DEBUG, "added %d args", added);
1103
1104         /* throw away output args which are just copies of the input args */
1105         for (i = 0; i < data->num_in_args; i++) {
1106             if (debug & DEBUG_AUTHOR_FLAG) {
1107                 report(LOG_DEBUG, "out_args[%d] = %s input copy discarded", 
1108                        i, out_args[i]);
1109             }
1110             free(out_args[i]);
1111             out_args[i] = NULL;
1112         }
1113
1114         /* Now compact the new args added to the end of the array down
1115            to the beginning */
1116
1117         j = 0;
1118         for (i = data->num_in_args; i < data->num_out_args; i++) {
1119             if (out_args[j]) /* we goofed */
1120                 report(LOG_ERR, "%s: out_args[%d] should be NULL", 
1121                        session.peer, j);
1122             if (!out_args[i]) /* we goofed */
1123                 report(LOG_ERR, "%s: out_args[%d] should not be NULL",
1124                        session.peer, i);
1125
1126             if (debug & DEBUG_AUTHOR_FLAG) {
1127                 report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]", 
1128                        i, out_args[i], j);
1129             }
1130             out_args[j++] = out_args[i];
1131             out_args[i] = NULL;
1132         }
1133         data->num_out_args = j;
1134         if (debug & DEBUG_AUTHOR_FLAG) {
1135             report(LOG_DEBUG, "%d output args", data->num_out_args);
1136         }
1137
1138         /* should/could do a realloc here but it won't matter */
1139         data->status = AUTHOR_STATUS_PASS_ADD;
1140         data->output_args = out_args;
1141
1142         /* free the server arguments */
1143         for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
1144             free(*cfg_argp);
1145         free(cfg_args);
1146
1147         return (0);
1148     }
1149
1150     /* no additions or replacements. Input and output are
1151        identical. Don't need to return anything */
1152
1153     if (debug & DEBUG_AUTHOR_FLAG) {
1154         report(LOG_DEBUG, "added %d", added);
1155     }
1156     data->status = AUTHOR_STATUS_PASS_ADD;
1157     if (out_args) {
1158         for (i = 0; i < data->num_out_args; i++) {
1159             free(out_args[i]);
1160         }
1161         free(out_args);
1162     }
1163
1164     /* Final sanity check */
1165     if (data->num_out_args != data->num_in_args) {
1166         data->status = AUTHOR_STATUS_ERROR;
1167         data->admin_msg = tac_strdup("Bad output arg cnt from do_author");
1168         report(LOG_ERR, "%s: Error %s", session.peer, data->admin_msg);
1169
1170         /* free the server arguments */
1171         for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
1172             free(*cfg_argp);
1173         free(cfg_args);
1174
1175         return (0);
1176     }
1177
1178     data->num_out_args = 0;
1179     data->output_args = NULL;
1180
1181     /* free the server arguments */
1182     for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
1183         free(*cfg_argp);
1184     free(cfg_args);
1185
1186     return (0);
1187 }
1188
1189 /* Return an integer indicating which kind of service is being
1190    requested. 
1191
1192    Conveniently this integer is one of our node types. If the service
1193    is a command authorisation request, also return the command name in
1194    cmdname. 
1195
1196    If this service is a ppp request, return the protocol name in
1197    protocol.
1198
1199    If this service is not one of the standard, known ones, return its
1200    name in svcname.
1201 */
1202
1203 static int
1204 get_nas_svc(data, cmdname, protocol, svcname)
1205 struct author_data *data;
1206 char **cmdname, **protocol, **svcname;
1207 {
1208     int i;
1209     char *nas_arg;
1210     
1211     *cmdname = NULL;
1212     *protocol = NULL;
1213     *svcname = NULL;
1214
1215     for (i = 0; i < data->num_in_args; i++) {
1216         nas_arg = data->input_args[i];
1217
1218         if (STREQ(nas_arg, "service=shell")) {
1219             for (i = 0; i < data->num_in_args; i++) {
1220                 nas_arg = data->input_args[i];
1221                 if (strncmp(nas_arg, "cmd", strlen("cmd")) == 0) {
1222                     /* A cmd=<nothing> means we are authorising exec startup */
1223                     if ((int)strlen(nas_arg) <= 4)
1224                         return (N_svc_exec);
1225
1226                     /* A non-null command means we are authorising a command */
1227                     *cmdname = nas_arg + strlen("cmd") + 1;
1228                     return (N_svc_cmd);
1229                 }
1230             }
1231             return(0);
1232         }
1233
1234         if (STREQ(nas_arg, "service=slip")) {
1235             return (N_svc_slip);
1236         }
1237         if (STREQ(nas_arg, "service=arap")) {
1238             return (N_svc_arap);
1239         }
1240         if (STREQ(nas_arg, "service=ppp")) {
1241             for (i = 0; i < data->num_in_args; i++) {
1242                 nas_arg = data->input_args[i];
1243                 if (strncmp(nas_arg, "protocol", strlen("protocol")) == 0) {
1244                     *protocol = nas_arg + strlen("protocol") + 1;
1245                     return(N_svc_ppp);
1246                 }
1247             }
1248         }
1249
1250         if (strncmp(nas_arg, "service=", strlen("service=")) ==0 ) {
1251             *svcname = nas_arg + strlen("service=");
1252             return(N_svc);
1253         }
1254     }
1255     return (0);
1256 }