dtcm: Coverity 89287
[oweals/cde.git] / cde / programs / dtaction / Main.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: Main.c /main/18 1998/07/23 17:56:36 mgreess $ */
24 /*****************************************************************************
25  *****************************************************************************
26  **
27  **   File:         Main.c
28  **
29  **   Project:      DT
30  **
31  **   Description:  This file contains the main program for dtaction.
32  **               
33  **
34  **(c) Copyright 1993, 1994 Hewlett-Packard Company
35  **(c) Copyright 1993, 1994 International Business Machines Corp.
36  **(c) Copyright 1993, 1994 Sun Microsystems, Inc.
37  **(c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
38  **
39  **
40  ****************************************************************************
41  ************************************<+>*************************************/
42
43 #include <stdlib.h>
44 #include <limits.h>
45 #include <stddef.h>
46 #include <unistd.h>
47 #include <nl_types.h>
48 #include <signal.h>
49 #include <locale.h>
50 #include <sys/param.h>          /* for MAXPATHLEN and MAXHOSTNAMELEN */
51
52 #if defined(sun) || defined(__linux__)
53 #include <crypt.h>
54 #include <shadow.h>
55 #endif
56
57 #include <errno.h>
58 #include <grp.h>
59 #include <pwd.h>
60
61 #include <X11/Intrinsic.h>
62 #include <Xm/XmP.h>
63 #include <Xm/Text.h>
64 #include <Xm/TextF.h>
65 #include <Xm/SelectioB.h>
66 #include <Xm/MessageB.h>
67 #include <Xm/Protocols.h>
68 #include <Xm/MwmUtil.h>
69
70 #include <Dt/Dt.h>
71 #include <Dt/DbUtil.h>
72 #include <Dt/CmdInv.h>
73 #include <Dt/Action.h>
74 #include <Dt/EnvControlP.h>
75
76
77 /******************************************************************************/
78
79 #include <time.h>
80
81
82 /* Command line options */
83 XrmOptionDescRec option_list[] =
84 {
85    {  "-user",     "user",          XrmoptionSepArg,   NULL},
86    {  "-contextDir", "contextDir",  XrmoptionSepArg,   NULL},
87    {  "-execHost", "execHost",      XrmoptionSepArg,   NULL},
88    {  "-termOpts", "termOpts",      XrmoptionSepArg,   NULL},
89 };
90
91 typedef struct {
92    char * user;
93    char * contextDir;
94    char * execHost;
95    char * termOpts;
96 } ApplArgs, *ApplArgsPtr;
97
98 #define LOGIN_STR_LEN 15
99 #define STAR ' '
100
101 Widget dlog;
102 char *password;
103 char *stars;
104 int  passwordLength = 0;
105
106 Boolean finished = False;
107 Boolean rootRequest = False;
108 char * toPword = NULL;
109 char * rootPword = NULL;
110 char * origName = "Unknown";
111 int basegid = -1;
112 int newuid = -1;
113
114 /* Dtaction resources */
115 XtResource resources[] =
116 {
117    {
118     "user", "User", XmRString, sizeof(char *),
119     XtOffsetOf(ApplArgs, user), XmRImmediate, (XtPointer) NULL,
120    },
121    {
122     "contextDir", "ContextDir", XmRString, sizeof(char *),
123     XtOffsetOf(ApplArgs, contextDir), XmRImmediate, (XtPointer) NULL,
124    },
125    {
126     "execHost", "ExecHost", XmRString, sizeof(char *),
127     XtOffsetOf(ApplArgs, execHost), XmRImmediate, (XtPointer) NULL,
128    },
129    {
130     "termOpts", "TermParms", XmRString, sizeof(char *),
131     XtOffsetOf(ApplArgs, termOpts), XmRImmediate, (XtPointer) NULL,
132    },
133 };
134
135  /*
136  * macro to get message catalog strings
137  */
138
139 #ifndef NO_MESSAGE_CATALOG
140 # ifdef __ultrix
141 #  define _CLIENT_CAT_NAME "dtact.cat"
142 # else  /* __ultrix */
143 #  define _CLIENT_CAT_NAME "dtact"
144 # endif /* __ultrix */
145 extern char *_DtGetMessage(char *filename, int set, int n, char *s);
146 # define GETMESSAGE(set, number, string)\
147     (_DtGetMessage(_CLIENT_CAT_NAME, set, number, string))
148 #else
149 # define GETMESSAGE(set, number, string)\
150     string
151 #endif
152
153 Boolean _DtWmStringsAreEqual( 
154                         char *in_str,
155                         char *test_str) ;
156 void CheckForDone( 
157                         XtPointer clientData,
158                         XtIntervalId id) ;
159 void CheckUserRequest( void ) ;
160 void CheckPasswd( void ) ;
161 void AddSuLog( 
162                         char *FromName,
163                         char *ToName,
164                         char *ChangeType) ;
165 void CleanPath( 
166                         char *Path) ;
167 void OkCallback( 
168                         Widget w,
169                         XtPointer clientData,
170                         XtPointer callData) ;
171 void ErrOkCallback( 
172                         Widget w,
173                         XtPointer clientData,
174                         XtPointer callData) ;
175 void CancelCallback( 
176                         Widget w,
177                         XtPointer clientData,
178                         XtPointer callData) ;
179 void MapCallback( 
180                         Widget w,
181                         XtPointer clientData,
182                         XtPointer callData) ;
183
184 void actionStatusCallback (
185                         DtActionInvocationID id,
186                         XtPointer   client_data,
187                         DtActionArg *aap,
188                         int         aac,
189                         DtActionStatus status );
190
191 void GetUserPrompt( void ) ;
192 void LogSuccess( void ) ;
193 void LogFailure( void ) ;
194 void MyInsert( 
195                         Widget w,
196                         XEvent *event,
197                         char **params,
198                         Cardinal *num_params) ;
199 void MyCancel( 
200                         Widget w,
201                         XEvent *event,
202                         char **params,
203                         Cardinal *num_params) ;
204 void EditPasswdCB( 
205                         Widget w,
206                         XtPointer client,
207                         XtPointer call) ;
208 void UnknownUser( void ) ;
209 void UnknownUserCallback( 
210                         Widget w,
211                         XtPointer clientData,
212                         XtPointer callData) ;
213
214
215 /********    End Forward Function Declarations    ********/
216
217
218 /***************************************************************************
219  *
220  *  Text widget actions and translations
221  *
222  ***************************************************************************/
223
224 XtActionsRec textActions[] = {
225         {"my-insert", (XtActionProc)MyInsert},
226         {"my-cancel", (XtActionProc)MyCancel},
227 };
228
229 char textEventBindings[] = {
230 "Shift <Key>Tab:                        prev-tab-group() \n\
231   Ctrl <Key>Tab:                        next-tab-group() \n\
232  <Key>Tab:                              next-tab-group() \n\
233  <Key>osfEndLine:                       end-of-line() \n\
234  <Key>osfBeginLine:                     beginning-of-line() \n\
235  ~Shift <Key>osfRight:                  forward-character()\n\
236  ~Shift <Key>osfLeft:                   backward-character()\n\
237   Ctrl <Key>osfDelete:                  delete-to-end-of-line()\n\
238  <Key>osfDelete:                        delete-next-character()\n\
239   <Key>osfBackSpace:                    delete-previous-character()\n\
240  <Key>osfActivate:                      activate()\n\
241   Ctrl <Key>Return:                     activate()\n\
242  <Key>Return:                           activate()\n\
243  <Key>osfCancel:                        my-cancel()\n\
244  <Key>:                                 my-insert()\n\
245  ~Ctrl ~Shift ~Meta ~Alt<Btn1Down>:     grab-focus() \n\
246  <EnterWindow>:                         enter()\n\
247  <LeaveWindow>:                         leave()\n\
248  <FocusIn>:                             focusIn()\n\
249  <FocusOut>:                            focusOut()\n\
250  <Unmap>:                               unmap()"
251 };
252
253
254 /****************************************************************************/
255 /****************************************************************************/
256
257 static Widget toplevel;
258 static ApplArgs appArgs;
259 static XtAppContext appContext;
260 static DtActionInvocationID actionId;
261 static Boolean exitAfterInvoked = False; 
262 static int exitStatus = 0;
263
264 /* ARGSUSED */
265 void
266 CheckForDone(
267    XtPointer clientData,
268    XtIntervalId id)
269 {
270     if ( toplevel->core.num_popups ==  0 ) 
271         exit(exitStatus);
272
273     XtAppAddTimeOut(appContext,
274         10, (XtTimerCallbackProc)CheckForDone,
275       NULL);
276 }
277
278
279 int
280 main( 
281      int argc,
282      char **argv ) 
283 {
284     Display *display;
285     Arg args[20];
286     int n=0;
287     char *actionName;
288     int numArgs = 0;
289     DtActionArg *ap = NULL;
290   
291     XtSetLanguageProc(NULL, NULL, NULL);
292     _DtEnvControl(DT_ENV_SET);
293     (void) signal(SIGCHLD, (void (*)())SIG_IGN);
294
295     /*  Initialize the toolkit and open the display  */
296     XtToolkitInitialize() ;
297     appContext = XtCreateApplicationContext() ;
298     if ( !(display = XtOpenDisplay( appContext, NULL, argv[0], "Dtaction", 
299                              option_list, 
300                              sizeof(option_list)/sizeof(XrmOptionDescRec),
301                              &argc, argv)) )
302     {
303         setlocale(LC_ALL, "");
304         fprintf(stderr, "%s", GETMESSAGE(1,11,"Can't open display.\n"));
305         exit(-1);
306     }
307   
308     XtSetArg(args[n], XmNallowShellResize, True); n++;
309     XtSetArg(args[n], XmNmappedWhenManaged, False); n++;
310     XtSetArg(args[n], XmNheight, 1); n++;
311     XtSetArg(args[n], XmNwidth, 1); n++;
312     toplevel = XtAppCreateShell( argv[0], "Dtaction", 
313             topLevelShellWidgetClass, display, args, n) ;
314
315     XtRealizeWidget(toplevel);
316
317     display = XtDisplay (toplevel);
318     XtGetApplicationResources(toplevel, &appArgs, 
319         resources, XtNumber(resources), NULL, 0);
320
321     password = XtMalloc(1);
322     password[0] = '\0';
323     stars = XtMalloc(1);
324     stars[0] = '\0';
325
326    /*  Get Dt initialized  */
327    if (DtInitialize (display, toplevel, argv[0], "Dtaction") == False)
328    {
329       /* Fatal Error: could not connect to the messaging system. */
330       /* DtInitialize() has already logged an appropriate error msg */
331       exit(-1);
332    }
333
334    /*
335     * If the request specified that it wanted to run as a different
336     * user, then take care of prompting for a password, and doing any
337     * necessary verification and logging.
338     */
339    CheckUserRequest();
340    
341    /* Load the filetype/action dbs; DtInvokeAction() requires this */
342    DtDbLoad();
343
344    /*
345     * Get the requested action name
346     */
347     if ( (actionName = argv[1]) == NULL)
348     {
349         fprintf(stderr, "%s", GETMESSAGE(1,10,"No action name specified.\n"));
350         exit(-1);
351     }
352
353     if ( argc > 2 ) 
354     {
355         /*
356          * create an action arg array for the file objects for
357          * this action.  This number of objects should be one
358          * less than the argument count. The returned vector will
359          * be terminated by a null pointer.
360          */
361         numArgs= argc - 2;
362         ap = (DtActionArg *) XtCalloc(numArgs,sizeof(DtActionArg)); 
363     }
364
365         /*
366          * This client is restricted to FILE arguments.
367          * for the time being.
368          */
369     for ( n = 0; n < numArgs; n++) {
370         ap[n].argClass    = DtACTION_FILE;
371         ap[n].u.file.name = argv[n+2];
372     }
373
374     actionId = DtActionInvoke(toplevel, actionName, ap, numArgs,
375         appArgs.termOpts,
376         appArgs.execHost,
377         appArgs.contextDir,
378         True,                   /* use indicator */
379         (DtActionCallbackProc) actionStatusCallback,
380         NULL);
381
382     /*
383      * Set up a timer if we didn't get a valid procId -- since there will
384      * be no invocation update in that case.
385      * We must invoke XtMainLoop() at least once, to force any prompt or
386      * error dialogs to get posted.
387      */
388     if ( !actionId)
389             XtAppAddTimeOut(appContext,
390                 10, (XtTimerCallbackProc)CheckForDone,
391                            NULL);
392
393     XtAppMainLoop(appContext);
394
395     return EXIT_SUCCESS;
396 }
397
398
399 static void
400 SetGidUid ( unsigned short rgid, unsigned short ruid )
401 {
402
403         /* fix process gid */
404 #if defined(SVR4) || defined(_AIX)
405     setgid(rgid);
406 #elif defined(__linux__) || defined(CSRG_BASED)
407     if(-1 == setregid(rgid, rgid)) {
408         fprintf(stderr, "SetGidUid: setregid failed on %d\n", rgid);
409     }
410 #elif defined(__hpux)
411     setresgid(rgid, rgid, rgid);
412 #else
413     setregid(rgid, rgid, rgid);
414 #endif
415
416         /* fix process uid */
417 #if defined (SVR4) || defined (_AIX)
418     setuid(ruid);
419 #elif defined(__linux__) || defined(CSRG_BASED)
420     if(-1 == setreuid(ruid, ruid)) {
421         fprintf(stderr, "SetGidUid: setreuid failed on %d\n", ruid);
422     }
423 #elif defined(__hpux)
424     setresuid(ruid, ruid, ruid);
425 #else
426     setreuid(ruid, ruid, ruid);
427 #endif
428
429 }
430
431
432 /*
433  * This function checks to see if the user has requested that the action
434  * be invoked under a different user Id.  If a different user Id has been
435  * requested, then the user will be prompted to enter either the password
436  * for that user, or the root password.  Once a valid password has been
437  * entered, this function will return.
438  */
439
440 void 
441 CheckUserRequest( void )
442
443 {
444    unsigned short ruid;
445    unsigned short rgid;
446    struct passwd * passwd;
447 #ifdef sun
448    struct spwd * spwd;
449 #endif
450    Boolean notExist = False;
451    Boolean alreadySetToRoot = False;
452
453    rootRequest = False;
454
455         /* get current group id */
456    rgid = getgid();
457         /* get current user id */
458    ruid = getuid();
459
460       /* See if the user wants to run as himself */
461    if (appArgs.user == NULL)
462    {
463       SetGidUid(rgid,ruid);
464       return;
465    }
466
467    /* Get password for the requested user */
468    passwd = getpwnam(appArgs.user);
469    if (passwd) {
470 #ifdef sun
471       spwd = getspnam(appArgs.user);
472       if (spwd)
473       {
474 #endif
475          if (passwd->pw_uid == ruid)
476          {
477             /* 
478              * We are already running as the
479              *  requested user.  So return now.
480              */
481              SetGidUid(rgid,ruid);
482              return;
483           }
484
485 #ifdef sun
486           if (spwd->sp_pwdp)
487              toPword = XtNewString(spwd->sp_pwdp);
488        }
489 #else
490        if (passwd->pw_passwd)
491           toPword = XtNewString(passwd->pw_passwd);
492 #endif /* sun */
493        basegid = passwd->pw_gid;
494        newuid = passwd->pw_uid;
495     }
496    else
497        notExist = True;
498
499    /* Root requests require some extra work later */
500    if (strcmp(appArgs.user, "root") == 0)
501          rootRequest = True;
502
503    /* Get name for the current user */
504    passwd = getpwuid(ruid);
505    if (passwd && passwd->pw_name)
506        origName = XtNewString(passwd->pw_name);
507
508    /* Get password for the root user */
509    passwd = getpwnam("root");
510    if (passwd && passwd->pw_passwd) 
511    {
512 #ifdef sun
513        spwd = getspnam("root");
514        if (spwd && spwd->sp_pwdp) 
515        { 
516            rootPword = XtNewString(spwd->sp_pwdp);
517        }
518 #else
519       rootPword = XtNewString(passwd->pw_passwd);
520 #endif /* sun */
521
522        if (passwd->pw_uid == ruid)
523           alreadySetToRoot = True;
524    }
525
526    /*
527     * If 'alreadySetToRoot' is set to True, then that means that the
528     * user is  currently running as root.
529     */
530
531    if (notExist)
532    {
533       /* Requested user does not exist; this function will exit() */
534        UnknownUser();
535    }
536    else if ((alreadySetToRoot) ||  /* requested users passwd is null */
537       ((toPword && (toPword[0] == '\0')) || (toPword == NULL)))
538    {
539        /* Already there -- no need to check a passwd */
540           LogSuccess();
541    }
542    else
543          CheckPasswd();
544 }
545
546
547 /**********
548  * void CheckPasswd ()
549  *
550  * get a password from the user and check it against an encrypted passwd
551  *
552  * Returns:
553  *      0 if invalid
554  *      True if valid
555  *
556  * Side Effects:
557  *      none.
558  */
559
560 void 
561 CheckPasswd( void )
562
563 {
564     /*
565      * get this users password
566      */
567     GetUserPrompt();
568 }
569
570
571 /**********
572  * void AddSuLog (FromName, ToName, ChangeType)
573  *
574  * add switch from user "FromName" to user "ToName" to sulog.
575  * ChangeType is "+" for success, "-" for failure.
576  *
577  * Parameters:
578  *      char *FromName -- from name (for logging).
579  *      char *ToName -- to name (for logging).
580  *      char *ChangeType -- +/- (for logging).
581  */
582
583 void 
584 AddSuLog(
585         char *FromName,
586         char *ToName,
587         char *ChangeType )
588
589 {
590     char        *toString;
591     struct tm  *localtime ();
592
593        FILE * f;
594     struct stat st;
595     time_t    timenow;
596     struct tm  *now;
597
598 #ifdef hpV4  /* 10.* versions */
599     char * SULog = "/var/adm/sulog";
600 #elif  defined( hpux )  /* 9.* versions */
601     char * SULog = "/usr/adm/sulog";
602 #else
603     char * SULog = "/var/adm/sulog";
604 #endif
605
606     if ((f = fopen (SULog, "a")) == NULL)
607         return;
608
609     (void) time (&timenow);
610     now = localtime (&timenow);
611
612     /* build toString... */
613     if (ToName && *ToName) 
614             toString = ToName;
615     else 
616             toString = FromName;
617
618     fprintf(f, (GETMESSAGE(1,1, 
619            "dtaction %1$.2d/%2$.2d %3$.2d:%4$.2d %5$1.1s %6$s %7$s-%8$s\n")),
620            now -> tm_mon + 1, now -> tm_mday, now -> tm_hour,
621            now -> tm_min, ChangeType, "?", FromName, toString);
622
623     (void) fclose (f);
624
625     /*
626      * take away write access from SULog
627      */
628
629     if(chmod (SULog, (int) (st.st_mode & 07777) & ~0222) == -1) {
630         fprintf(stderr, "Error on chmod of '%s', %s\n", SULog, strerror(errno));
631     }
632
633     return;
634 }
635
636
637 /**********
638  * void CleanPath (Path);
639  *
640  * remove any directories from the path that are
641  *    - null (leading/trailing colon or double colon)
642  *    - not anchored to root (no leading /)
643  *
644  * the returned path is the original path with any such
645  * directories stripped
646  *
647  * Parameters:
648  *      char *Path -- $PATH to clean
649  *
650  * Returns:
651  *      none.
652  *
653  * Side Effects:
654  *      Unanchored paths will be stripped off of Path.
655  */
656
657 void 
658 CleanPath(
659         char *Path )
660
661 {
662     char  *StrippedPath;
663     char  *PathHead;
664
665     StrippedPath = PathHead = Path;
666
667     while (*Path) {
668
669         /*
670          * remove multiple ':'s
671          */
672
673         while (*Path && (*Path == ':')) {
674             (void) Path++;
675         }
676
677         /*
678          * is the first character of this
679          * directory a '/'????
680          */
681
682         if (*Path == '/') {
683
684             /*
685              * copy directory intact;
686              */
687
688             while (*Path && (*Path != ':')) {
689                 *StrippedPath++ = *Path++;
690             }
691
692             if (*Path == ':') {
693                 *StrippedPath++ = *Path++;
694             }
695         }
696         else {
697
698             /*
699              * zip past directory
700              */
701
702             while (*Path && (*Path != ':')) {
703                 (void) Path++;
704             }
705
706             if (*Path == ':') {
707                 (void) Path++;
708             }
709         }
710
711     }
712
713     /*
714      * remove all trailing ':'s
715      */
716
717     while ((StrippedPath > PathHead) && (StrippedPath[-1] == ':')) {
718         StrippedPath--;
719     }
720
721     /*
722      * null terminate the path
723      */
724
725     *StrippedPath = '\0';
726     return;
727 }
728
729
730 /*
731  * This is the Ok callback for the password dialog.  It will attempt to
732  * validate the password.  If invalid, then an error dialog is posted,
733  * and the user is prompted to try again.  If valid, then the process
734  * will change to run as the requested user, and dtaction will continue
735  * on its way, attempting to invoke the requested action.
736  */
737
738 /* ARGSUSED */
739 void 
740 OkCallback(
741         Widget w,
742         XtPointer clientData,
743         XtPointer callData )
744
745 {
746    Boolean valid = False;
747
748    /* Do any verification here */
749
750    /* check for primary passwd... */
751    if (!strcmp (crypt (password, toPword), toPword))
752       valid = True;
753
754    /* check for secondary passwd ... */
755    if (rootPword && *rootPword &&
756        !strcmp (crypt (password, rootPword), rootPword))
757    {
758       valid = True;
759    }
760    else if (((rootPword == NULL) || (*rootPword == '\0')) &&
761             (*password == '\0'))
762    {
763       /* No root password, and the user entered no password */
764       valid = True;
765    }
766
767
768    /* If valid password, then unpost */
769    if (valid)
770    {
771       XtUnmanageChild(dlog);
772       XFlush(XtDisplay(dlog));
773       finished = True;
774       LogSuccess();
775    }
776    else
777    {
778       /* Invalid password */
779       Widget err;
780       int n;
781       Arg args[10];
782       XmString okLabel;
783       XmString message;
784       char * template;
785       char * title;
786       char * master;
787
788       okLabel = XmStringCreateLocalized(GETMESSAGE(1, 2, "OK"));
789       template = (GETMESSAGE(1,3, "The password you entered does not match\nthe password for user %s.\n\nPlease reenter the password, or select the\nCancel button to terminate the operation."));
790       master = XtMalloc(strlen(template) + strlen(appArgs.user) + 10);
791       sprintf(master, template, appArgs.user);
792       message = XmStringCreateLocalized(master);
793       title = (GETMESSAGE(1,4, "Action Invoker - Password Error"));
794
795       /* Post an error dialog */
796       n = 0;
797       XtSetArg(args[n], XmNtitle, title); n++;
798       XtSetArg(args[n], XmNmessageString, message); n++;
799       XtSetArg(args[n], XmNokLabelString, okLabel); n++;
800       err = XmCreateErrorDialog(dlog, "err", args, n);
801       XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_CANCEL_BUTTON));
802       XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_HELP_BUTTON));
803       XtManageChild(err);
804       XtAddCallback(err, XmNokCallback, ErrOkCallback, err);
805
806       XFlush(XtDisplay(dlog));
807       XmUpdateDisplay(dlog);
808       LogFailure();
809       XmStringFree(okLabel);
810       XmStringFree(message);
811       XtFree(master);
812    }
813 }
814
815
816 /*
817  * This is the Cancel callback for the password dialog.  It will unpost
818  * the dialog, and exit.
819  */
820
821 /* ARGSUSED */
822 void 
823 CancelCallback(
824         Widget w,
825         XtPointer clientData,
826         XtPointer callData )
827
828 {
829    XtUnmanageChild(dlog);
830    XFlush(XtDisplay(dlog));
831    exit(-1);
832 }
833
834
835 /*
836  * This is the 'Ok' callback for the invalid password error dialog.
837  * It will simply unpost and destroy the error dialog.
838  */
839
840 /* ARGSUSED */
841 void 
842 ErrOkCallback(
843         Widget w,
844         XtPointer clientData,
845         XtPointer callData )
846
847 {
848    Widget err = (Widget)clientData;
849
850    XtUnmanageChild(err);
851    XFlush(XtDisplay(err));
852    XmUpdateDisplay(err);
853    XtDestroyWidget(err);
854 }
855
856
857 /*
858  * This callback is invoked when the password dialog is posted; it forces
859  * the focus to the text input field.
860  */
861
862 /* ARGSUSED */
863 void 
864 MapCallback(
865         Widget w,
866         XtPointer clientData,
867         XtPointer callData )
868
869 {
870    Widget text;
871    Widget dlog = (Widget)clientData;
872
873    /* Force focus initially to the text field */
874    text = XmSelectionBoxGetChild(dlog, XmDIALOG_TEXT);
875    XmProcessTraversal(text, XmTRAVERSE_CURRENT);
876 }
877
878
879 /*
880  * This function creates the prompt dialog for collecting the password
881  * from the user.  It will not give up control until a valid password
882  * has been entered.  If the user cancels the request, then the cancel
883  * callback will exit.
884  */
885
886 void 
887 GetUserPrompt( void )
888
889 {
890    int n;
891    XmString xmString;
892    XmString xmString2;
893    char prompt[BUFSIZ];
894    Widget help;
895    Widget text;
896    XEvent event;
897    Arg args[10];
898    XtTranslations      textTable;
899    XmString cancelLabel;
900    XmString okLabel;
901
902    snprintf(prompt, sizeof prompt, (GETMESSAGE(1,5, "Enter password for user %s:")), 
903             appArgs.user);
904    xmString = XmStringCreateLocalized(prompt);
905    xmString2 =XmStringCreateLocalized(GETMESSAGE(1,6, "Action Invoker - Password"));
906    cancelLabel = XmStringCreateLocalized(GETMESSAGE(1,7, "Cancel"));
907    okLabel = XmStringCreateLocalized(GETMESSAGE(1,2, "OK"));
908
909    XtAppAddActions(appContext,textActions, 2);
910    textTable = XtParseTranslationTable(textEventBindings);
911
912    /* Create the prompt dialog */
913    n = 0;
914    XtSetArg(args[n], XmNselectionLabelString, xmString);  n++;
915    XtSetArg(args[n], XmNdialogTitle, xmString2);  n++;
916    XtSetArg(args[n], XmNautoUnmanage, False);  n++;
917    XtSetArg(args[n], XmNokLabelString, okLabel);  n++;
918    XtSetArg(args[n], XmNcancelLabelString, cancelLabel);  n++;
919    XtSetArg(args[n], XmNdefaultPosition, False);  n++;
920    dlog = XmCreatePromptDialog(toplevel, "prompt", args, n);
921    XmStringFree(xmString);
922    XmStringFree(xmString2);
923    XmStringFree(okLabel);
924    XmStringFree(cancelLabel);
925    XtAddCallback(dlog, XmNokCallback, OkCallback, NULL);
926    XtAddCallback(dlog, XmNcancelCallback, CancelCallback, NULL);
927
928    text = XmSelectionBoxGetChild(dlog, XmDIALOG_TEXT);
929    n = 0;
930    XtSetArg(args[n], XmNtranslations, textTable); n++;
931    XtSetArg(args[n], XmNverifyBell, False);       n++;
932    XtSetValues(text, args, n);
933    XtAddCallback(text, XmNmodifyVerifyCallback, EditPasswdCB, NULL);
934
935    /* Add callback for forcing traversal to the text field */
936    XtAddCallback (XtParent(dlog), XmNpopupCallback, MapCallback, dlog);
937
938    /* Unmanage the help button */
939    help = XmSelectionBoxGetChild(dlog, XmDIALOG_HELP_BUTTON);
940    XtUnmanageChild(help);
941
942    /* Center the dialog */
943    XtRealizeWidget(dlog);
944    XtSetArg (args[0], XmNx,
945              (Position)(WidthOfScreen(XtScreen(dlog)) -
946                         dlog->core.width) / 2);
947    XtSetArg (args[1], XmNy,
948              (Position)(HeightOfScreen(XtScreen(dlog)) -
949                         dlog->core.height) / 2);
950    XtSetValues (dlog, args, 2);
951
952    /* Set the transient property */
953    XSetTransientForHint (XtDisplay (toplevel),
954                          XtWindow (XtParent (dlog)),
955                          XtWindow (toplevel));
956
957    /*  Adjust the decorations for the dialog shell of the dialog  */
958    n = 0;
959    XtSetArg(args[n], XmNmwmFunctions, 0);          n++;
960    XtSetArg(args[n], XmNmwmDecorations, 
961             MWM_DECOR_BORDER | MWM_DECOR_TITLE);   n++;
962    XtSetValues(XtParent(dlog), args, n);
963
964    /* Post the dialog */
965    XtManageChild(dlog);
966
967    /* Wait for the user to finish with the dialog */
968    while (!finished)
969    {
970       XtAppNextEvent(appContext,&event);
971       XtDispatchEvent(&event);
972    }
973
974    /* Destroy the widget, and return any data back to the appl */
975    XtDestroyWidget(dlog);
976 }
977
978
979 /*
980  * When a user has successfully logged in as another user, we need to set
981  * the uid and gid to the requested user.  In addition, if the user is
982  * changing to 'root', then we need to log this in /usr/adm/sulog, and
983  * we need to do some housekeeping of the $PATH environment variable.
984  */
985
986 void 
987 LogSuccess( void )
988
989 {
990    char * path;
991    char * tmpPath;
992
993    AddSuLog(origName, appArgs.user, "+");
994
995    if (rootRequest)
996    {
997       /* Special stuff for the root user */
998       /* Cleanse the $PATH setting */
999       tmpPath = getenv("PATH");
1000       path = XtNewString(tmpPath);
1001       CleanPath (path);
1002       tmpPath = XtMalloc(strlen(path) + 10);
1003       strcpy(tmpPath, "PATH=");
1004       strcat(tmpPath, path);
1005       putenv(tmpPath);
1006    }
1007
1008    /* Set up the user's new id's */
1009    SetGidUid(basegid,newuid);
1010    initgroups(appArgs.user, basegid);
1011
1012 }
1013
1014
1015 /*
1016  * Each time the user enters an invalid password, we need to log this in
1017  * /usr/adm/sulog, if the user is attempting to switch to the 'root' user.
1018  */
1019
1020 void 
1021 LogFailure( void )
1022
1023 {
1024    /* Unable to change to specified user; post error, then exit */
1025    AddSuLog(origName, appArgs.user, "-");
1026 }
1027
1028
1029 /***************************************************************************
1030  *
1031  *  MyInsert
1032  *
1033  *  Local self-insert action for the text widget. The default action
1034  *  discards control characters, which are allowed in password.
1035  ***************************************************************************/
1036
1037 /* ARGSUSED */
1038 void 
1039 MyInsert(
1040         Widget w,
1041         XEvent *event,
1042         char **params,
1043         Cardinal *num_params )
1044 {
1045     char           str[32];
1046     XComposeStatus compstatus;
1047     int            n;
1048
1049     n = XLookupString((XKeyEvent *)event, str, sizeof(str),
1050                       (KeySym *)NULL, &compstatus);
1051
1052     if (n > 0) {
1053        str[n] = '\0';
1054        XmTextFieldInsert(w, XmTextFieldGetInsertionPosition(w), str);
1055     }
1056 }
1057
1058
1059 /***************************************************************************
1060  *
1061  *  MyCancel
1062  *
1063  *  This action catches the 'Escape' key, and following Motif standards,
1064  *  unposts the dialog, as if the 'Cancel' button had been pressed.
1065  ***************************************************************************/
1066
1067 /* ARGSUSED */
1068 void 
1069 MyCancel(
1070         Widget w,
1071         XEvent *event,
1072         char **params,
1073         Cardinal *num_params )
1074 {
1075    CancelCallback(w, NULL, NULL);
1076 }
1077
1078
1079
1080 /***************************************************************************
1081  *
1082  *  EditPasswdCB
1083  *
1084  *  implement no-echo of the password
1085  ***************************************************************************/
1086
1087
1088 /* ARGSUSED */
1089 void 
1090 EditPasswdCB(
1091         Widget w,
1092         XtPointer client,
1093         XtPointer call )
1094 {
1095
1096     static Boolean      allow_flag = False;
1097     int                 replaced_length, i;
1098     char                *src, *dst;
1099     XmTextVerifyPtr     cbData = (XmTextVerifyPtr) call;
1100   
1101     if (!allow_flag)
1102     {
1103       /* 
1104        * we need to keep track of the password ourselves in order to 
1105        * disable echoing of the password...
1106        */
1107       
1108       replaced_length = cbData->endPos - cbData->startPos;
1109       if (replaced_length > cbData->text->length)
1110       {
1111         /* shift the text at and after endPos to the left...  */
1112         for (src = password + cbData->endPos,
1113              dst = src + (cbData->text->length - replaced_length),
1114              i = passwordLength - cbData->endPos;
1115              i > 0;
1116              ++src, ++dst, --i)
1117          {
1118             *dst = *src;
1119          }
1120       }
1121       else if (replaced_length < cbData->text->length)
1122       {
1123         /* Buffer must grow */
1124         password = XtRealloc(password,
1125               passwordLength + cbData->text->length - replaced_length + 5);
1126
1127         /* shift the text at and after endPos to the right...  */
1128         for (src = password + passwordLength - 1,
1129              dst = src + (cbData->text->length - replaced_length),
1130              i = passwordLength - cbData->endPos;
1131              i > 0;
1132              --src, --dst, --i)
1133          {
1134             *dst = *src;
1135          }
1136       }
1137       
1138       /*
1139        * update the password...
1140        */
1141
1142       for (src = cbData->text->ptr,
1143            dst = password + cbData->startPos,
1144            i = cbData->text->length;
1145            i > 0;
1146            ++src, ++dst, --i)
1147       {
1148         *dst = *src;
1149       }
1150
1151       passwordLength += cbData->text->length - replaced_length;
1152       password[passwordLength] = '\0';
1153       stars = XtRealloc(stars, cbData->text->length + 10);
1154       for (i = 0; i < cbData->text->length; i++)
1155          stars[i] = ' ';
1156       stars[cbData->text->length] = '\0';
1157       
1158       /*
1159        * put the appropriate number of stars in the passwd Widget..
1160        */
1161
1162       allow_flag = True;
1163       XmTextFieldReplace(w, cbData->startPos, cbData->endPos, stars);
1164       allow_flag = False;
1165     }
1166   
1167     cbData->doit = allow_flag;
1168 }
1169
1170
1171 /*
1172  * This function posts an error dialog informing the user that they have
1173  * specified an invalid user name.  No further processing will be done; we
1174  * will simply wait for the user to acknowledge the error, and then exit.
1175  */
1176
1177 void 
1178 UnknownUser( void )
1179
1180 {
1181    Widget err;
1182    int n;
1183    Arg args[10];
1184    XmString okLabel;
1185    XmString message;
1186    char * template;
1187    char * title;
1188    char * master;
1189
1190    okLabel = XmStringCreateLocalized(GETMESSAGE(1,2, "OK"));
1191    template = (GETMESSAGE(1,8, "The user '%s' is an unknown user name.\n\nThe requested action will not be executed."));
1192    master = XtMalloc(strlen(template) + strlen(appArgs.user) + 10);
1193    sprintf(master, template, appArgs.user);
1194    message = XmStringCreateLocalized(master);
1195    title = (GETMESSAGE(1,9, "Action Invoker - Unknown User"));
1196
1197    /* Post an error dialog */
1198    n = 0;
1199    XtSetArg(args[n], XmNtitle, title); n++;
1200    XtSetArg(args[n], XmNmessageString, message); n++;
1201    XtSetArg(args[n], XmNokLabelString, okLabel); n++;
1202    XtSetArg(args[n], XmNdefaultPosition, False);  n++;
1203    err = XmCreateErrorDialog(toplevel, "err", args, n);
1204    XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_CANCEL_BUTTON));
1205    XtUnmanageChild(XmMessageBoxGetChild(err, XmDIALOG_HELP_BUTTON));
1206
1207    /* Center the dialog */
1208    XtRealizeWidget(err);
1209    XtSetArg (args[0], XmNx,
1210              (Position)(WidthOfScreen(XtScreen(err)) -
1211                         err->core.width) / 2);
1212    XtSetArg (args[1], XmNy,
1213              (Position)(HeightOfScreen(XtScreen(err)) -
1214                         err->core.height) / 2);
1215    XtSetValues (err, args, 2);
1216
1217    XtManageChild(err);
1218    XtAddCallback(err, XmNokCallback, UnknownUserCallback, err);
1219    XtAddCallback(err, XmNcancelCallback, UnknownUserCallback, err);
1220
1221    XFlush(XtDisplay(toplevel));
1222    XmUpdateDisplay(toplevel);
1223    LogFailure();
1224    XmStringFree(okLabel);
1225    XmStringFree(message);
1226    XtFree(master);
1227    XtAppMainLoop(appContext);
1228 }
1229
1230
1231 /*
1232  * This is the 'Cancel' callback for the 'Invalid User' error dialog.
1233  * It removes the dialog, and then exits.
1234  */
1235
1236 /* ARGSUSED */
1237 void 
1238 UnknownUserCallback(
1239         Widget w,
1240         XtPointer clientData,
1241         XtPointer callData )
1242
1243 {
1244    Widget err = (Widget)clientData;
1245
1246    XtUnmanageChild(err);
1247    XFlush(XtDisplay(err));
1248    XmUpdateDisplay(err);
1249    exit(-1);
1250 }
1251
1252 void actionStatusCallback(
1253                         DtActionInvocationID id,
1254                         XtPointer   client_data,
1255                         DtActionArg *aap,
1256                         int         aac,
1257                         DtActionStatus status )
1258 {
1259
1260     switch( status ) {
1261         case DtACTION_INVOKED:
1262             /*
1263              * There may still be error dialogs to post so we must return
1264              * to mainLoop before exiting.
1265              */
1266             if ( exitAfterInvoked )
1267                     XtAppAddTimeOut(appContext,
1268                         10 , (XtTimerCallbackProc)CheckForDone,
1269                       NULL);
1270             break;
1271
1272         case DtACTION_DONE:
1273             XtAppAddTimeOut(appContext,
1274                 10 , (XtTimerCallbackProc)CheckForDone,
1275               NULL);
1276             break;
1277
1278         case DtACTION_FAILED:
1279         case DtACTION_CANCELED:
1280             exitStatus = 1;
1281             XtAppAddTimeOut(appContext,
1282                 10 , (XtTimerCallbackProc)CheckForDone,
1283               NULL);
1284             break;
1285
1286         default:
1287             break;
1288     }
1289
1290 }