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