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