Release bumped to "gts2".
[tac_plus.git] / do_author.c
index 574736f..3f23602 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Copyright (c) 1995-1998 by Cisco systems, Inc.
 
    Permission to use, copy, modify, and distribute this software for
    FITNESS FOR A PARTICULAR PURPOSE.
 */
 
+
 #include "tac_plus.h"
-#include "regexp.h"
 
-static int get_nas_svc();
-static int authorize_cmd();
-static int authorize_exec();
-static int authorize_svc();
-static void post_authorization();
-static int pre_authorization();
+#include <stdlib.h>
+#include <string.h>
+
+#include "do_author.h"
+#include "tac_regexp.h"
+#include "cfgfile.h"
+#include "report.h"
+#include "utils.h"
+#include "programs.h"
+#include "main.h"
+#include "parse.h"
+#include "cfgeval.h"
+
+#ifdef MAXSESS
+#include "maxsess.h"
+#endif
+#ifdef USE_PAM
+#include "tac_pam.h"
+#endif
+
+
+static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
+static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
+static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
+static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
+static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
+static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
+
+
+int do_author TAC_ARGS((struct author_data *data));
 
 /* Return 0 is data->status is valid */
 int
@@ -37,7 +61,7 @@ struct author_data *data;
     int svc;
     char *cmd, *protocol, *svcname;
 #ifdef USE_PAM
-    char *pam_service= NULL;
+    const char *pam_service= NULL;
 #endif
     protocol = NULL;
 
@@ -48,46 +72,48 @@ struct author_data *data;
 
     /* If this user doesn't exist in our configs, do the default */
 
-    if (!cfg_user_exists(username) && !cfg_user_exists(DEFAULT_USERNAME)) {
-       
-       if (cfg_no_user_permitted()) {
+    if (!cfg_user_exists(username)) {
+       if (!cfg_user_exists(DEFAULT_USERNAME)) {
+
+           if (cfg_no_user_permitted()) {
+               if (debug & DEBUG_AUTHOR_FLAG)
+                   report(LOG_DEBUG,
+                          "user '%s' or '%s' not found, permitted by default",
+                          username, DEFAULT_USERNAME);
+
+               data->status = AUTHOR_STATUS_PASS_ADD;
+               data->output_args = NULL;
+               data->num_out_args = 0;
+               return (0);
+           }
+
            if (debug & DEBUG_AUTHOR_FLAG)
-               report(LOG_DEBUG, 
-                      "user '%s' or '%s' not found, permitted by default", 
+               report(LOG_DEBUG,
+                      "user '%s' or '%s' not found, denied by default",
                       username, DEFAULT_USERNAME);
-
-           data->status = AUTHOR_STATUS_PASS_ADD;
-           data->output_args = NULL;
-           data->num_out_args = 0;
+           data->status = AUTHOR_STATUS_FAIL;
            return (0);
        }
 
-       if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, 
-                  "user '%s' or '%s' not found, denied by default", 
-                  username, DEFAULT_USERNAME);
-       data->status = AUTHOR_STATUS_FAIL;
-       return (0);
-    }
+       /* assumed (cfg_user_exists(DEFAULT_USERNAME)): */
 
-    if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
        if (debug & DEBUG_AUTHOR_FLAG) {
-           report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'", 
+           report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'",
                   DEFAULT_USERNAME, username);
        }
        username = DEFAULT_USERNAME;
     }
 
     /* See if there's a program defined which will do authorization for us */
-    if (pre_authorization(username, data)) 
+    if (pre_authorization(username, data))
        return(0);
-    
-    /* 
+
+    /*
      * Decide what kind of authorization request this is. Currently
      * one of: exec, cmd, slip, arap, ppp or <string>
-     * 
+     *
      * If it's a command typed to the exec, return its text in cmd.
-     * 
+     *
      * If it's a ppp request, return the protocol name in protocol.
      */
 
@@ -125,25 +151,26 @@ struct author_data *data;
 #endif /* MAXSESS */
 
 #ifdef USE_PAM
-       /* Check  PAM Authorization */
-     switch (svc) {
+    /* Check  PAM Authorization */
+    switch (svc) {
+
     case N_svc_ppp:
     case N_svc_exec:
-if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
+       if ((pam_service = cfg_get_pam_service(data->id->username, TAC_PLUS_RECURSE) )) {
 
-       if (debug & DEBUG_AUTHOR_FLAG)
+           if (debug & DEBUG_AUTHOR_FLAG)
                report(LOG_DEBUG, "PAM Authorization begin for user %s",data->id->username);
-       if(tac_pam_authorization(data->id->username,data,pam_service))
-       {
-               if (debug & DEBUG_AUTHOR_FLAG)
-                       report(LOG_DEBUG, "PAM Authorization Fail");
+           if (tac_pam_authorization(data->id->username, data, pam_service)) {
+               if (debug & DEBUG_AUTHOR_FLAG)
+                   report(LOG_DEBUG, "PAM Authorization Fail");
                return(0);
-       }
-} /* Pam_service */
+           }
+       } /* Pam_service */
+
     default:
        break;
     }
-#endif 
+#endif
 
     switch(svc) {
     default:
@@ -155,9 +182,9 @@ if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
        /* A command authorisation request */
        status = authorize_cmd(username, cmd, data);
        break;
+
     case N_svc_exec:
-       if (authorize_exec(username, data)) 
+       if (authorize_exec(username, data))
            return(0);
        /* FALLTHRU */
 
@@ -173,23 +200,25 @@ if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
     }
 
     post_authorization(username, data);
-    return(status);    
+    return(status);
 }
 
-/* If an before-authorization program has been specified, call it. 
+/* If an before-authorization program has been specified, call it.
 
    A return value of 1 means no further authorization is required
 */
 
+static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
+
 static int
-pre_authorization(username, data) 
-char *username;
+pre_authorization(username, data)
+const char *username;
 struct author_data *data;
 {
     int status;
     char **out_args;
     int out_cnt, i;
-    char *cmd;
+    const char *cmd;
     char error_str[255];
     int error_len = 255;
 
@@ -199,9 +228,9 @@ struct author_data *data;
     /* If a before-authorization program exists, call it to see how to
        proceed */
 
-    cmd = cfg_get_pvalue(username, TAC_IS_USER, 
+    cmd = cfg_get_pvalue(S_user, username,
                         S_before, TAC_PLUS_RECURSE);
-    if (!cmd) 
+    if (!cmd)
        return(0);
 
     if (debug & DEBUG_AUTHOR_FLAG)
@@ -213,11 +242,11 @@ struct author_data *data;
     switch (status) {
     default:
        if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)", 
+           report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)",
                   cmd, status);
 
        data->status = AUTHOR_STATUS_ERROR;
-       data->admin_msg = 
+       data->admin_msg =
            tac_strdup("Illegal return status from pre-authorization command");
        data->msg = tac_strdup(error_str);
        data->num_out_args = 0;
@@ -247,10 +276,10 @@ struct author_data *data;
            free(out_args);
        }
        return(1);
-           
+
     case 1: /* Deny */
        if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)", 
+           report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
                   cmd, status);
 
        data->status = AUTHOR_STATUS_FAIL;
@@ -269,10 +298,10 @@ struct author_data *data;
 
     case 2: /* Use replacement AV pairs from program as final result */
        if (debug & DEBUG_AUTHOR_FLAG) {
-           report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)", 
+           report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)",
                   cmd, status);
            for(i=0; i < out_cnt; i++)
-               report(LOG_DEBUG, "%s", out_args[i]); 
+               report(LOG_DEBUG, "%s", out_args[i]);
        }
 
        /* and install the new set of AV pairs as output */
@@ -283,10 +312,10 @@ struct author_data *data;
 
     case 3: /* deny, but return attributes and server-msg to NAS */
        if (debug & DEBUG_AUTHOR_FLAG) {
-           report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)", 
+           report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)",
                   cmd, status);
            for(i=0; i < out_cnt; i++)
-               report(LOG_DEBUG, "%s", out_args[i]); 
+               report(LOG_DEBUG, "%s", out_args[i]);
        }
 
        /* and install the new set of AV pairs as output */
@@ -303,17 +332,18 @@ struct author_data *data;
    change the authorization status by calling an external program.
 */
 
+static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
+
 static void
-post_authorization(username, data) 
-    char *username;
-    struct author_data *data;
+post_authorization(username, data)
+const char *username;
+struct author_data *data;
 {
     char **out_args;
     int out_cnt, i;
     int status;
-    char *after = cfg_get_pvalue(username, TAC_IS_USER, 
-                               S_after, TAC_PLUS_RECURSE);
-    if (!after) 
+    const char *after = cfg_get_pvalue(S_user, username, S_after, TAC_PLUS_RECURSE);
+    if (!after)
        return;
 
     if (debug & DEBUG_AUTHOR_FLAG)
@@ -332,7 +362,7 @@ post_authorization(username, data)
     switch (status) {
     default:
        if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, 
+           report(LOG_DEBUG,
                   "cmd %s returns %d (Error)", after, status);
 
        data->status = AUTHOR_STATUS_ERROR;
@@ -342,10 +372,10 @@ post_authorization(username, data)
        if (debug & DEBUG_AUTHOR_FLAG)
            report(LOG_DEBUG, "cmd %s returns 0 (no change)", after);
        return;
-           
+
     case 1:                            /* Deny */
        if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)", 
+           report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
                   after, status);
 
        data->status = AUTHOR_STATUS_FAIL;
@@ -354,7 +384,7 @@ post_authorization(username, data)
     case 2:
        /* Use replacement AV pairs from program */
        if (debug & DEBUG_AUTHOR_FLAG)
-           report(LOG_DEBUG, "cmd %s returns 2 (replace & continue)", 
+           report(LOG_DEBUG, "cmd %s returns 2 (replace & continue)",
                   after);
 
        /* Free any existing AV output pairs */
@@ -378,6 +408,8 @@ post_authorization(username, data)
 }
 
 
+static char *value TAC_ARGS((char *s));
+
 /* Return a pointer to the value part of an attr=value string */
 static char *
 value(s)
@@ -393,6 +425,8 @@ char *s;
 /* Reassemble the command arguments as typed by the user, out of the
    array of args we received. Return "" if there are no arguments */
 
+static char *assemble_args TAC_ARGS((struct author_data *data));
+
 static char *
 assemble_args(data)
 struct author_data *data;
@@ -445,31 +479,28 @@ struct author_data *data;
    Otherwise, we return 1, indicating no further processing is
    required for this request. */
 
+static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
+
 static int
 authorize_exec(user, data)
-char *user;
+const char *user;
 struct author_data *data;
 {
-    NODE *svc;
-
-    if (debug & DEBUG_AUTHOR_FLAG) 
+    if (debug & DEBUG_AUTHOR_FLAG)
        report(LOG_DEBUG, "exec authorization request for %s", user);
 
     /* Is an exec explicitly configured? If so, return 0 so we know to
        process its attributes */
 
-    svc = cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE);
-    if (svc) {
-       if (debug & DEBUG_AUTHOR_FLAG) 
-           report(LOG_DEBUG, "exec is explicitly permitted by line %d",
-                  svc->line);
+    if (cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+       if (debug & DEBUG_AUTHOR_FLAG)
+           report(LOG_DEBUG, "exec is explicitly permitted");
        return (0);
     }
 
     /* No exec is configured. Are any commands configured? */
-    svc = cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE);
-    if (svc) {
-       if (debug & DEBUG_AUTHOR_FLAG) 
+    if (cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+       if (debug & DEBUG_AUTHOR_FLAG)
            report(LOG_DEBUG, "exec permitted because commands are configured");
 
        data->status = AUTHOR_STATUS_PASS_ADD;
@@ -478,19 +509,7 @@ struct author_data *data;
        return (1);
     }
 
-    /* No exec or commands configured. What's the default? */
-    if (cfg_user_svc_default_is_permit(user)) {
-
-       if (debug & DEBUG_AUTHOR_FLAG) 
-           report(LOG_DEBUG, "exec permitted by default");
-
-       data->status = AUTHOR_STATUS_PASS_ADD;
-       data->output_args = NULL;
-       data->num_out_args = 0;
-       return (1);
-    }
-
-    if (debug & DEBUG_AUTHOR_FLAG) 
+    if (debug & DEBUG_AUTHOR_FLAG)
        report(LOG_DEBUG, "exec denied by default");
 
     data->status = AUTHOR_STATUS_FAIL;
@@ -498,19 +517,18 @@ struct author_data *data;
     return(1);
 }
 
-/* Is an exec command authorized per our database(s)?  
+/* Is an exec command authorized per our database(s)?
    Return 0 if status is valid */
 
+static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
+
 static int
 authorize_cmd(user, cmd, data)
-char *user, *cmd;
+const char *user;
+const char *cmd;
 struct author_data *data;
 {
-    NODE *node;
     char *args;
-    int match;
-
-    args = assemble_args(data);
 
     if (!cmd) {
        data->status = AUTHOR_STATUS_ERROR;
@@ -520,93 +538,41 @@ struct author_data *data;
        return (0);
     }
 
-    node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE);
-
-    /* The command does not exist. Do the default */
-    if (!node) {
-
-       if (cfg_user_svc_default_is_permit(user)) {
-
-           if (debug & DEBUG_AUTHOR_FLAG) 
-               report(LOG_DEBUG, "cmd %s does not exist, permitted by default",
-                      cmd);
-           
-           data->status = AUTHOR_STATUS_PASS_ADD;
-           data->num_out_args = 0;
-           if (args)
-               free(args);
-           return(0);
-       }
+    args = assemble_args(data);
+    switch (cfg_authorize_cmd(user, cmd, args)) {
 
-       if (debug & DEBUG_AUTHOR_FLAG) 
-           report(LOG_DEBUG, "cmd %s does not exist, denied by default",
-                  cmd);
+    case ER_TRUE:
+       data->status = AUTHOR_STATUS_PASS_ADD;
+       data->num_out_args = 0;
+       break;
 
+    case ER_FALSE:
        data->status = AUTHOR_STATUS_FAIL;
        data->num_out_args = 0;
-       if (args)
-           free(args);
-       return(0);
-    }
-
-    /* The command exists. The default if nothing matches is DENY */
-    data->status = AUTHOR_STATUS_FAIL;
-    data->num_out_args = 0;
-
-    for (node=node->value1; node && args; node = node->next) {
-       match = regexec((regexp *) node->value1, args);
-
-       if (debug & DEBUG_AUTHOR_FLAG) {
-           report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
-                  node->line, cmd,
-                  node->type == N_permit ? "permit" : "deny",
-                  node->value, args, (match ? "" : "no "));
-       }
-
-       if (!match)
-           continue;
+       break;
 
-       switch (node->type) {
-       case N_permit:
-           if (debug & DEBUG_AUTHOR_FLAG) {
-               report(LOG_DEBUG, "%s %s permitted by line %d", 
-                      cmd, args, node->line);
-           }
-           data->status = AUTHOR_STATUS_PASS_ADD;
-           data->num_out_args = 0;
-           break;
-       case N_deny:
-           if (debug & DEBUG_AUTHOR_FLAG) {
-               report(LOG_DEBUG, "%s %s denied by line %d", 
-                      cmd, args, node->line);
-           }
-           data->status = AUTHOR_STATUS_FAIL;
-           data->num_out_args = 0;
-           break;
-       default:
-           data->status = AUTHOR_STATUS_ERROR;
-           data->admin_msg = tac_strdup("Server error illegal configuration node");
-           report(LOG_ERR, "%s: %s %s %s", 
-                  session.peer, cmd, args, data->admin_msg);
-           break;
-       }
-       if (args)
-           free(args);
-       args = NULL;
-       return (0);
+    case ER_UNKNOWN:
+       data->status = AUTHOR_STATUS_ERROR;
+       data->admin_msg = tac_strdup("Server error illegal configuration");
+       break;
     }
+
     if (args)
        free(args);
-    return (0);
+    return(0);
 }
 
+static int is_separator TAC_ARGS((int ch));
+
 static int
 is_separator(ch)
-char ch;
+int ch;                                /* promoted "char" type */
 {
     return (ch == '=' || ch == '*');
 }
 
+static int arg_ok TAC_ARGS((char *arg));
+
 /* check an attr=value pair for well-formedness */
 static int
 arg_ok(arg)
@@ -630,6 +596,8 @@ char *arg;
 }
 
 
+static int match_attrs TAC_ARGS((char *nas_arg, char *server_arg));
+
 /* return 1 if attrs match, 0 otherwise */
 static int
 match_attrs(nas_arg, server_arg)
@@ -647,12 +615,14 @@ char *nas_arg, *server_arg;
     return (0);
 }
 
+static int match_values TAC_ARGS((char *nas_arg, char *server_arg));
+
 /* return 1 if values match, 0 otherwise */
 static int
 match_values(nas_arg, server_arg)
 char *nas_arg, *server_arg;
 {
-    while (*nas_arg && 
+    while (*nas_arg &&
           *server_arg &&
           !is_separator(*nas_arg)) {
        nas_arg++;
@@ -666,11 +636,13 @@ char *nas_arg, *server_arg;
     nas_arg++;
     if (*server_arg)
        server_arg++;
-    
+
     /* compare values */
     return(STREQ(nas_arg, server_arg));
 }
 
+static int mandatory TAC_ARGS((char *arg));
+
 /* Return 1 if arg is mandatory, 0 otherwise */
 static int
 mandatory(arg)
@@ -683,13 +655,15 @@ char *arg;
 
     /* if we're not at the end, this must be the separator */
     if (*p && !is_separator(*p)) {
-       report(LOG_ERR, "%s: Error on arg %s cannot find separator", 
+       report(LOG_ERR, "%s: Error on arg %s cannot find separator",
               session.peer, arg);
        return (0);
     }
     return (*p == '=');
 }
 
+static int optional TAC_ARGS((char *arg));
+
 static int
 optional(arg)
 char *arg;
@@ -699,29 +673,24 @@ char *arg;
 
 /* PPP-LCP requests are a special case. If they are not explicitly
    configured, but there are other ppp services explicitly configured,
-   we admit (return 0) any PPP-LCP request */
+   we admit (return 1) any PPP-LCP request */
 
-static int 
-ppp_lcp_allowed(svc, protocol, user)
-    int svc;
-    char *user, *protocol;
-{
-    /* This is not a ppp/lcp request. Just Say No */
-    if (!(svc == N_svc_ppp &&
-         protocol &&
-         STREQ(protocol, "lcp")))
-       return(0);
+static int ppp_lcp_allowed TAC_ARGS((const char *user));
 
-    /* It is an LCP request. Are there PPP services configured */
-    if (cfg_ppp_is_configured(user, TAC_PLUS_RECURSE)) {
+static int
+ppp_lcp_allowed(user)
+const char *user;
+{
+    /* It is an LCP request. Are there PPP services configured? */
+    if (cfg_get_svc_node(user, N_svc_ppp, NULL /* protocol */, NULL /* svcname */, TAC_PLUS_RECURSE, NULL /* nodep */)) {
        if (debug & DEBUG_AUTHOR_FLAG) {
-           report(LOG_DEBUG, 
+           report(LOG_DEBUG,
                   "ppp/lcp request permitted (ppp is configured for %s)",
                   user);
        }
        return(1);
     }
-    
+
     /* It is an LCP request but no PPP services are configured */
     if (debug & DEBUG_AUTHOR_FLAG) {
        report(LOG_DEBUG, "ppp/lcp request denied (ppp not configured for %s)",
@@ -730,14 +699,16 @@ ppp_lcp_allowed(svc, protocol, user)
     return(0);
 }
 
+static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
+
 /* Return 0 means data->status is valid */
 static int
 authorize_svc(user, svc, protocol, svcname, data)
-    char *user;
-    int svc;
-    char *svcname;
-    struct author_data *data;
-    char *protocol; /* valid only if svc == ppp */
+const char *user;
+int svc;
+const char *svcname;
+struct author_data *data;
+const char *protocol;          /* valid only if svc == ppp */
 {
     int max_args;
     char **out_args, **outp;
@@ -747,35 +718,44 @@ authorize_svc(user, svc, protocol, svcname, data)
     char **cfg_argp;
     int deny_by_default;
     NODE *node;
+    int service_permit;
 
     int replaced = 0;
     int added = 0;
     int cfg_cnt;
 
     /* Does this service exist? */
-    node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE);
+    service_permit = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE, &node);
 
-    if (!node) {
-       /* Service not found. If the default is permit, or this is an
-          PPP/LCP request and other ppp services are configured,
-          we'll allow it. */
+    /* Now we may have three cases:
+     * service_permit == 1 && node != NULL
+     * service_permit == 1 && node == NULL
+     * service_permit == 0    (BTW node == NULL)
+     */
 
-       if (cfg_user_svc_default_is_permit(user)) {
+    if (service_permit && !node) {
+       /* Service not found and the default is permit, we'll allow it. */
 
-           if (debug & DEBUG_AUTHOR_FLAG)
-               report(LOG_DEBUG, 
-              "svc=%s protocol=%s svcname=%s not found, permitted by default", 
-                      cfg_nodestring(svc), 
-                      protocol ? protocol : "",
-                      svcname ? svcname : "");
+       if (debug & DEBUG_AUTHOR_FLAG)
+           report(LOG_DEBUG,
+          "svc=%s protocol=%s svcname=%s not found, permitted by default",
+                  cfg_nodestring(svc),
+                  protocol ? protocol : "",
+                  svcname ? svcname : "");
 
-           data->status = AUTHOR_STATUS_PASS_ADD;
-           data->num_out_args = 0;
-           data->output_args = NULL;
-           return(0);
+       data->status = AUTHOR_STATUS_PASS_ADD;
+       data->num_out_args = 0;
+       data->output_args = NULL;
+       return(0);
        }
 
-       if (ppp_lcp_allowed(svc, protocol, user)) {
+    if (!service_permit) {
+       /* Service not found, if this is an
+          PPP/LCP request and other ppp services are configured,
+          we'll allow it. */
+
+       if (svc == N_svc_ppp && protocol && STREQ(protocol, "lcp")
+        && ppp_lcp_allowed(user)) {
            data->status = AUTHOR_STATUS_PASS_ADD;
            data->num_out_args = 0;
            data->output_args = NULL;
@@ -791,7 +771,7 @@ authorize_svc(user, svc, protocol, svcname, data)
        data->output_args = NULL;
        return(0);
     }
-    
+
     /* Get server args configured in the config file. */
     cfg_args = cfg_get_svc_attrs(node, &deny_by_default);
 
@@ -896,10 +876,10 @@ authorize_svc(user, svc, protocol, svcname, data)
                        free(out_args[i]);
                    free(out_args);
                }
-               
+
                data->num_out_args = 0;
                data->output_args = NULL;
-               
+
                /* free the server arguments */
                for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
                    free(*cfg_argp);
@@ -911,7 +891,7 @@ authorize_svc(user, svc, protocol, svcname, data)
               the output */
 
            if (debug & DEBUG_AUTHOR_FLAG) {
-               report(LOG_DEBUG, 
+               report(LOG_DEBUG,
                       "nas:%s, svr:absent, default=permit -> add %s (d)",
                       nas_arg, nas_arg);
            }
@@ -948,7 +928,7 @@ authorize_svc(user, svc, protocol, svcname, data)
            /* f). If not found, look for the first attribute match in
               the mandatory list. If found, add DAEMONS's AV pair to
               output */
-           
+
            for (j = 0; j < cfg_cnt; j++) {
                cfg_arg = cfg_args[j];
                if (optional(cfg_arg))
@@ -976,8 +956,8 @@ authorize_svc(user, svc, protocol, svcname, data)
                cfg_arg = cfg_args[j];
                if (!optional(cfg_arg))
                    continue;
-               
-               if (match_attrs(nas_arg, cfg_arg) && 
+
+               if (match_attrs(nas_arg, cfg_arg) &&
                    match_values(nas_arg, cfg_arg)) {
                    if (debug & DEBUG_AUTHOR_FLAG) {
                        report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (g)",
@@ -999,7 +979,7 @@ authorize_svc(user, svc, protocol, svcname, data)
                cfg_arg = cfg_args[j];
                if (!optional(cfg_arg))
                    continue;
-               
+
                if (match_attrs(nas_arg, cfg_arg)) {
                    if (debug & DEBUG_AUTHOR_FLAG) {
                        report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (h)",
@@ -1039,7 +1019,7 @@ authorize_svc(user, svc, protocol, svcname, data)
 
     }
 
-    
+
     /* k). After all AV pairs have been processed, for each mandatory
        DAEMON AV pair, if there is no attribute match already in the
        output list, add the AV pair (add only one AV pair for each
@@ -1049,7 +1029,7 @@ authorize_svc(user, svc, protocol, svcname, data)
        cfg_arg = cfg_args[i];
 
        if (!mandatory(cfg_arg))
-           continue;   
+           continue;
 
        for (j = 0; j < data->num_out_args; j++) {
            char *output_arg = out_args[j];
@@ -1071,9 +1051,9 @@ authorize_svc(user, svc, protocol, svcname, data)
        data->num_out_args++;
 
     next_cfg_arg:;
-       
+
     }
-       
+
     /* If we replaced or deleted some pairs we must return the entire
        list we've constructed */
 
@@ -1098,13 +1078,13 @@ authorize_svc(user, svc, protocol, svcname, data)
 
     if (added) {
 
-       if (debug & DEBUG_AUTHOR_FLAG) 
+       if (debug & DEBUG_AUTHOR_FLAG)
            report(LOG_DEBUG, "added %d args", added);
 
        /* throw away output args which are just copies of the input args */
        for (i = 0; i < data->num_in_args; i++) {
            if (debug & DEBUG_AUTHOR_FLAG) {
-               report(LOG_DEBUG, "out_args[%d] = %s input copy discarded", 
+               report(LOG_DEBUG, "out_args[%d] = %s input copy discarded",
                       i, out_args[i]);
            }
            free(out_args[i]);
@@ -1117,14 +1097,14 @@ authorize_svc(user, svc, protocol, svcname, data)
        j = 0;
        for (i = data->num_in_args; i < data->num_out_args; i++) {
            if (out_args[j]) /* we goofed */
-               report(LOG_ERR, "%s: out_args[%d] should be NULL", 
+               report(LOG_ERR, "%s: out_args[%d] should be NULL",
                       session.peer, j);
            if (!out_args[i]) /* we goofed */
                report(LOG_ERR, "%s: out_args[%d] should not be NULL",
                       session.peer, i);
 
            if (debug & DEBUG_AUTHOR_FLAG) {
-               report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]", 
+               report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]",
                       i, out_args[i], j);
            }
            out_args[j++] = out_args[i];
@@ -1187,11 +1167,11 @@ authorize_svc(user, svc, protocol, svcname, data)
 }
 
 /* Return an integer indicating which kind of service is being
-   requested. 
+   requested.
 
    Conveniently this integer is one of our node types. If the service
    is a command authorisation request, also return the command name in
-   cmdname. 
+   cmdname.
 
    If this service is a ppp request, return the protocol name in
    protocol.
@@ -1200,6 +1180,8 @@ authorize_svc(user, svc, protocol, svcname, data)
    name in svcname.
 */
 
+static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
+
 static int
 get_nas_svc(data, cmdname, protocol, svcname)
 struct author_data *data;
@@ -1207,7 +1189,7 @@ char **cmdname, **protocol, **svcname;
 {
     int i;
     char *nas_arg;
-    
+
     *cmdname = NULL;
     *protocol = NULL;
     *svcname = NULL;