2
0

get_level.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  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. /* $XConsortium: get_level.c /main/4 1995/10/27 16:19:39 rswiston $ */
  24. /*
  25. * get_level.c
  26. * last modified by:
  27. * David Dolson June 7/92
  28. * - rewrote most of B1 security routines. Much of it is based on
  29. * parallel routines in login.
  30. * Ron Voll July 7/92
  31. * - rolled the xdm version of this file into dtlogin.
  32. */
  33. #ifdef BLS /* Brackets entire file */
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #include <signal.h>
  37. #include <pwd.h>
  38. #include <grp.h>
  39. #include <stdio.h>
  40. #include <termio.h>
  41. #include <errno.h>
  42. #ifdef SEC_NET_TTY
  43. #include <sys/socket.h>
  44. #include <netinet/in.h>
  45. #include <netdb.h>
  46. #endif
  47. #include <sys/security.h>
  48. #include <sys/audit.h>
  49. #include <prot.h>
  50. #include <protcmd.h>
  51. #if defined(TAC3) && !defined(TPLOGIN)
  52. #include <sys/secpolicy.h>
  53. #include <mandatory.h>
  54. #include <fcntl.h>
  55. #endif
  56. #include <stdlib.h>
  57. #include <time.h>
  58. #include <sys/ioctl.h>
  59. #include <unistd.h>
  60. #include <string.h>
  61. #if 0
  62. #include <strings.h>
  63. #endif
  64. #include <sys/wait.h>
  65. #include <grp.h>
  66. /*
  67. * Local include file for bls specific definitions.
  68. * Also defines some of the structures from dm.h for bls usage.
  69. */
  70. #include "bls.h"
  71. /* drop those privs from the base set which are not needed by xdm */
  72. void
  73. drop_privs(void)
  74. {
  75. priv_t privs[SEC_SPRIVVEC_SIZE];
  76. getpriv(SEC_BASE_PRIV, privs);
  77. RMBIT(privs, SEC_ALLOWNETACCESS);
  78. RMBIT(privs, SEC_NETPRIVSESSION);
  79. RMBIT(privs, SEC_NETNOAUTH);
  80. RMBIT(privs, SEC_MULTILEVELDIR);
  81. setpriv(SEC_BASE_PRIV, privs);
  82. return;
  83. }
  84. /* stuff to do at the start */
  85. void
  86. init_security(void)
  87. {
  88. /* set default file creation mode to be restrictive */
  89. umask(~SEC_DEFAULT_MODE);
  90. drop_privs();
  91. }
  92. /* check that the requested security level is valid for this user,
  93. * return 1 = success, return 0 is fail(fatal)
  94. */
  95. int
  96. verify_user_seclevel( struct verify_info verify, char *desired_label)
  97. {
  98. int uid;
  99. mand_ir_t *dl_ir, *clearance_ir;
  100. struct pr_passwd *prpwd;
  101. struct passwd *pwd;
  102. prpwd = verify->prpwd;
  103. pwd = verify->pwd;
  104. uid = verify->uid;
  105. /* check that desired_label falls within user's range */
  106. dl_ir = mand_er_to_ir(desired_label);
  107. if (dl_ir ==NULL) {
  108. audit_login(prpwd, pwd, verify->terminal,
  109. "Unknown clearance level", ES_LOGIN_FAILED);
  110. Debug("unable to translate clearance\n");
  111. return 0;
  112. }
  113. /* get user clearance from prpwd database */
  114. if (prpwd->uflg.fg_clearance)
  115. clearance_ir = &prpwd->ufld.fd_clearance;
  116. else if (prpwd->sflg.fg_clearance)
  117. clearance_ir = &prpwd->sfld.fd_clearance;
  118. else
  119. clearance_ir = mand_syslo;
  120. /* make sure clearance dominates or equals desired_label */
  121. switch(mand_ir_relationship(/* subject */ dl_ir,
  122. /* object */ clearance_ir)) {
  123. case MAND_ODOM:
  124. case MAND_EQUAL:
  125. /* Within range */
  126. break;
  127. default:
  128. audit_login(prpwd, pwd, verify->terminal,
  129. "Security label out of range", ES_LOGIN_FAILED);
  130. Debug("Invalid clearance for this user\n");
  131. mand_free_ir(dl_ir);
  132. return 0;
  133. }
  134. verify->clearance_ir = clearance_ir;
  135. verify->sec_label_ir = dl_ir;
  136. return 1;
  137. }
  138. /* check the proper structures to determine if the user has a password.
  139. * If the nullpw field is set, the user does not need one, and this
  140. * overrides the rest of the checking.
  141. * return 1 means that a password exists (or is not needed)
  142. */
  143. int
  144. password_exists(struct verify_info *verify)
  145. {
  146. struct pr_passwd *prpwd;
  147. BOOL nocheck;
  148. Debug("password_exists()\n");
  149. prpwd = verify->prpwd;
  150. if (prpwd->uflg.fg_nullpw)
  151. nocheck=prpwd->ufld.fd_nullpw;
  152. else if (prpwd->sflg.fg_nullpw)
  153. nocheck=prpwd->sfld.fd_nullpw;
  154. else
  155. nocheck=FALSE;
  156. if (!nocheck) { /* user needs password */
  157. Debug("password required for user\n");
  158. if (!prpwd->uflg.fg_encrypt ||
  159. prpwd->ufld.fd_encrypt[0] == '\0' ) {
  160. return 0;
  161. }
  162. }
  163. return 1;
  164. }
  165. /* check that the requested security level can be used on this X terminal,
  166. * and that it is not locked.
  167. * Currently there is no support for locking xterms like there is for
  168. * /dev/tty* terminals.
  169. */
  170. int
  171. verify_sec_xterm(struct verify_info *verify, char *desired_label)
  172. {
  173. return 1;
  174. }
  175. /* set clearance and label for the user. Audit all failures.
  176. * return 0=fail, 1=pass
  177. */
  178. int
  179. set_sec_label(struct verify_info *verify)
  180. {
  181. struct pr_passwd *prpwd;
  182. struct passwd *pwd;
  183. /* set clearance */
  184. prpwd = verify->prpwd;
  185. pwd = verify->pwd;
  186. if (setclrnce(verify->sec_label_ir)==-1) {
  187. switch(errno) {
  188. case EPERM:
  189. audit_login(prpwd, pwd, verify->terminal,
  190. "Insufficient privs to set clearance", ES_LOGIN_FAILED);
  191. Debug ("login failed: EPERM on setclrnce()\n");
  192. break;
  193. case EFAULT:
  194. /* audit:login failed: xdm memory fault */
  195. default:
  196. audit_login(prpwd, pwd, verify->terminal,
  197. "Unable to set clearance", ES_LOGIN_FAILED);
  198. Debug ("setclrnce failed: error: %d\n", errno);
  199. break;
  200. }
  201. return 0;
  202. }
  203. /* set label */
  204. if (setslabel(verify->sec_label_ir)==-1) {
  205. switch(errno) {
  206. case EPERM:
  207. audit_login(prpwd, pwd, verify->terminal,
  208. "Insufficient privs to set sec label", ES_LOGIN_FAILED);
  209. Debug ("login failed: insufficient priv. to setslabel()\n");
  210. break;
  211. case EFAULT:
  212. /* audit:login failed: xdm memory fault */
  213. default:
  214. audit_login(prpwd, pwd, verify->terminal,
  215. "Unable to set sec label", ES_LOGIN_FAILED);
  216. Debug ("setslabel() failed: error: %d\n", errno);
  217. break;
  218. }
  219. return 0;
  220. }
  221. return 1;
  222. }
  223. /* set the effective, base, and maximum priv vectors for the
  224. * new process, based on values from the pr_passwd entry.
  225. * Inability to find either user priv's or default priv's
  226. * results in failure. One or the other must be there.
  227. * Function returns 1 for success, 0 for failure.
  228. * A failure of this function should be considered fatal.
  229. */
  230. int
  231. set_sec_privs(struct verify_info *verify)
  232. {
  233. priv_t *maxprivs, *bprivs;
  234. priv_t curr_bprivs[SEC_SPRIVVEC_SIZE];
  235. priv_t curr_sprivs[SEC_SPRIVVEC_SIZE];
  236. struct pr_passwd *prpwd;
  237. struct passwd *pwd;
  238. int bit;
  239. prpwd = verify->prpwd;
  240. pwd = verify->pwd;
  241. /* kernel authorizations */
  242. if (prpwd->uflg.fg_sprivs) {
  243. maxprivs = &prpwd->ufld.fd_sprivs[0];
  244. }else if(prpwd->sflg.fg_sprivs) {
  245. maxprivs = &prpwd->sfld.fd_sprivs[0];
  246. Debug("Using default kernel priv's\n");
  247. }else {
  248. audit_login(prpwd, pwd, verify->terminal,
  249. "Unable to find kernel priv set for user",
  250. ES_LOGIN_FAILED);
  251. Debug("Can't find max. priv set for user-quitting\n");
  252. return 0;
  253. }
  254. /* base priv's and initial effective priv's */
  255. if (verify->prpwd->uflg.fg_bprivs) {
  256. bprivs = &verify->prpwd->ufld.fd_bprivs[0];
  257. }else if (verify->prpwd->sflg.fg_bprivs) { /* use system defaults */
  258. bprivs = &verify->prpwd->sfld.fd_bprivs[0];
  259. Debug("Using default base priv's\n");
  260. }else{
  261. audit_login(prpwd, pwd, verify->terminal,
  262. "Unable to find base priv set for user",
  263. ES_LOGIN_FAILED);
  264. Debug("Can't find base priv set for user-quitting\n");
  265. return 0;
  266. }
  267. getpriv(SEC_MAXIMUM_PRIV, curr_sprivs);
  268. getpriv(SEC_BASE_PRIV, curr_bprivs);
  269. /* remove those privs which the current process does not have,
  270. * to avoid any error in the following system calls
  271. */
  272. for (bit=0; bit<=SEC_MAX_SPRIV; bit++) {
  273. if (!ISBITSET(curr_sprivs, bit))
  274. RMBIT(maxprivs, bit);
  275. if (!ISBITSET(curr_bprivs, bit))
  276. RMBIT(bprivs, bit);
  277. }
  278. /* login removes those bits from maxprivs which the current process
  279. * does not have. - This program assumes the system config
  280. * utilities will enforce the rules for setpriv(3). Any failure
  281. * of setpriv will indicate a corrupt database.
  282. */
  283. if (setpriv(SEC_MAXIMUM_PRIV, maxprivs)==-1) {
  284. switch(errno) {
  285. case EPERM:
  286. Debug("setpriv (max) failed: EPERM\n");
  287. break;
  288. case EINVAL:
  289. Debug("setpriv (max) failed: EINVAL\n");
  290. break;
  291. case EFAULT:
  292. Debug("setpriv (max) failed: EFAULT\n");
  293. break;
  294. default:
  295. Debug("setpriv (max) failed for unknown error: %d\n",errno);
  296. break;
  297. }
  298. audit_login(prpwd, pwd, verify->terminal,
  299. "Unable to set Kernel privs", ES_LOGIN_FAILED);
  300. Debug("Unable to set Kernel privs (error %d): aborting\n",errno);
  301. return 0;
  302. }
  303. if (setpriv(SEC_BASE_PRIV, bprivs)==-1) {
  304. switch(errno) {
  305. case EPERM:
  306. Debug("setpriv (base) failed: EPERM\n");
  307. break;
  308. case EINVAL:
  309. Debug("setpriv (base) failed: EINVAL\n");
  310. break;
  311. case EFAULT:
  312. Debug("setpriv (base) failed: EFAULT\n");
  313. break;
  314. default:
  315. Debug("setpriv (base) failed for unknown error: %d\n",errno);
  316. break;
  317. }
  318. audit_login(prpwd, pwd, verify->terminal,
  319. "Unable to set base privs", ES_LOGIN_FAILED);
  320. return 0;
  321. }
  322. if (setpriv(SEC_EFFECTIVE_PRIV, bprivs)==-1) {
  323. switch(errno) {
  324. case EPERM:
  325. Debug("setpriv (effective) failed: EPERM\n");
  326. break;
  327. case EINVAL:
  328. Debug("setpriv (effective) failed: EINVAL\n");
  329. break;
  330. case EFAULT:
  331. Debug("setpriv (effective) failed: EFAULT\n");
  332. break;
  333. default:
  334. Debug("setpriv (effective) failed for unknown error: %d\n",
  335. errno);
  336. break;
  337. }
  338. audit_login(prpwd, pwd, verify->terminal,
  339. "Unable to set effective privs", ES_LOGIN_FAILED);
  340. Debug("Unable to set effective privs (error %d): aborting\n",errno);
  341. return 0;
  342. }
  343. return 1;
  344. }
  345. /* change the current process over to be owned by the user verify->uid.
  346. * Also properly set the privs, sec label, etc.
  347. * Also audits failures.
  348. * return=1 for success, 0 for fail. A failure should be considered fatal.
  349. */
  350. int
  351. change_to_user(struct verify_info *verify)
  352. {
  353. struct pr_passwd *prpwd;
  354. struct passwd *pwd;
  355. int new_nice;
  356. prpwd = verify->prpwd;
  357. pwd = verify->pwd;
  358. Debug("change_to_user()\n");
  359. /* 1. set the login user id - settable only once */
  360. if (setluid(verify->uid)==-1) {
  361. switch(errno) {
  362. case EPERM:
  363. Debug("Unable to set luid - EPERM\n");
  364. audit_login(prpwd, pwd, verify->terminal,
  365. "Unable to set luid - insufficient privs",
  366. ES_LOGIN_FAILED);
  367. break;
  368. case EINVAL:
  369. Debug("Unable to set luid - suspicious of pwd db.\n");
  370. audit_login(prpwd, pwd, verify->terminal,
  371. "Unable to set luid - out of range", ES_LOGIN_FAILED);
  372. break;
  373. default:
  374. Debug("Can't set luid-Unknown error %d\n",errno);
  375. audit_login(prpwd, pwd, verify->terminal,
  376. "Unable to set luid-unknown error", ES_LOGIN_FAILED);
  377. break;
  378. }
  379. return 0;
  380. }
  381. /*
  382. * Set the 'nice' priority if necessary. Since the return value
  383. * of nice(2) can normally be -1 from the documentation, and
  384. * -1 is the error condition, we key off of errno, not the
  385. * return value to find if the change were successful.
  386. * Note we must do this before the setuid(2) below.
  387. */
  388. errno = 0;
  389. prpwd = verify->prpwd;
  390. if (prpwd->uflg.fg_nice)
  391. new_nice = prpwd->ufld.fd_nice;
  392. else if (prpwd->sflg.fg_nice)
  393. new_nice = prpwd->sfld.fd_nice;
  394. if (prpwd->uflg.fg_nice || prpwd->sflg.fg_nice) {
  395. (void) nice(new_nice);
  396. if (errno != 0) {
  397. audit_login(prpwd, verify->pwd, NULL,
  398. "bad 'nice' setting", ES_LOGIN_FAILED);
  399. Debug("Bad priority setting.\n");
  400. return 0;
  401. }
  402. }
  403. /* 2. set the group(s) id and
  404. * 3. set the regular user id */
  405. #ifdef NGROUPS
  406. /* setgroups (verify->ngroups, verify->groups);
  407. */
  408. if(setgid (verify->groups[0])) {
  409. switch(errno) {
  410. case EPERM:
  411. Debug("setgid EPERM\n");
  412. break;
  413. case EINVAL:
  414. Debug("setgid EINVAL\n");
  415. break;
  416. default:
  417. Debug("setgid unknown error: %d\n",errno);
  418. break;
  419. }
  420. return 0;
  421. }
  422. initgroups(verify->user_name, verify->groups[0]);
  423. #else
  424. if(setgid (verify->gid)) {
  425. switch(errno) {
  426. case EPERM: Debug("setgid EPERM\n");break;
  427. case EINVAL: Debug("setgid EINVAL\n");break;
  428. default: Debug("setgid unknown error\n");break;
  429. }
  430. return 0;
  431. }
  432. #endif
  433. if(setuid (verify->uid)) {
  434. switch(errno) {
  435. case EPERM: Debug("setgid EPERM\n");break;
  436. case EINVAL: Debug("setgid EINVAL\n");break;
  437. default: Debug("setgid unknown error\n");break;
  438. }
  439. return 0;
  440. }
  441. /* 4. set security clearance and label for the new process */
  442. if (!set_sec_label(verify))
  443. return 0;
  444. /* 5. set audit parameters */
  445. audit_adjust_mask(prpwd);
  446. /* 6. set privlege levels - maximum, base, and effective */
  447. if (!set_sec_privs(verify))
  448. return 0;
  449. return 1;
  450. }
  451. /*
  452. * Try to read back everything, and print it. If a fatal error occurs,
  453. * return code is 0. 1=success.
  454. */
  455. int
  456. dump_sec_debug_info(struct verify_info *verify)
  457. {
  458. mand_ir_t *level_ir;
  459. priv_t privs[SEC_SPRIVVEC_SIZE];
  460. Debug ("luid: %d, real uid: %d, effective uid:%d,\n",
  461. getluid(),getuid(),geteuid());
  462. Debug ("real gid:%d, effective gid: %d\n", getgid(),getegid());
  463. level_ir = mand_alloc_ir();
  464. if (getclrnce(level_ir)==-1) {
  465. switch(errno) {
  466. case EFAULT: Debug("getclrnce EFAULT\n");break;
  467. case EINVAL: Debug("getclrnce EINVAL\n");break;
  468. default: Debug("getclrnce unknown error:%d\n",errno);break;
  469. }
  470. return 0;
  471. }else Debug ("Clearance: %s\n", mand_ir_to_er(level_ir) );
  472. if (getslabel(level_ir)==-1) {
  473. switch(errno) {
  474. case EFAULT: Debug("getslabel EFAULT\n");break;
  475. case EINVAL: Debug("getslabel EINVAL\n");break;
  476. default: Debug("getslabel unknown error:%d\n",errno);break;
  477. }
  478. return 0;
  479. }else Debug ("Level: %s\n", mand_ir_to_er(level_ir));
  480. mand_free_ir(level_ir);
  481. if(getpriv(SEC_MAXIMUM_PRIV, privs)==-1) {
  482. switch(errno) {
  483. case EFAULT: Debug("getpriv max EFAULT\n");break;
  484. case EINVAL: Debug("getpriv max EINVAL\n");break;
  485. default: Debug("getpriv max unknown error:%d\n",errno);
  486. break;
  487. }
  488. return 0;
  489. }else Debug ("max priv: %x.%x\n", privs[0],privs[1]);
  490. if(getpriv(SEC_EFFECTIVE_PRIV, privs)==-1) {
  491. switch(errno) {
  492. case EFAULT: Debug("getpriv eff EFAULT\n");break;
  493. case EINVAL: Debug("getpriv eff EINVAL\n");break;
  494. default: Debug("getpriv eff unknown error:%d\n",errno);
  495. break;
  496. }
  497. return 0;
  498. }else Debug ("eff priv: %x.%x\n", privs[0],privs[1]);
  499. if(getpriv(SEC_BASE_PRIV, privs)==-1) {
  500. switch(errno) {
  501. case EFAULT: Debug("getpriv base EFAULT\n");break;
  502. case EINVAL: Debug("getpriv base EINVAL\n");break;
  503. default: Debug("getpriv base unknown error:%d\n",errno);
  504. break;
  505. }
  506. return 0;
  507. }else Debug ("base priv: %x.%x\n", privs[0],privs[1]);
  508. return 1;
  509. }
  510. /*
  511. * writeLoginInfo
  512. * Input: file name string (ex. $HOME/.dtlogininfo)
  513. * verify structure with password stuff
  514. * Write login information to a file to be displayed later, after a
  515. * successful login.
  516. *
  517. * Xsession will need to be modified something like this...
  518. * DTHELLO="$DTDIR/bin/dthello -f /etc/copyright -f $HOME/.dtlogininfo"
  519. */
  520. int
  521. writeLoginInfo( char *filename, struct verify_info *verify)
  522. {
  523. char *s1="Last successful login: %s";
  524. char *s2="Last unsuccessful login: %s";
  525. char *s3;
  526. char s[80];
  527. char term[15];
  528. char *label;
  529. char *message="Sensitivity level for process: ";
  530. int i;
  531. int nl;
  532. time_t slogin, ulogin;
  533. char *slabel;
  534. char *uterminal, *sterminal;
  535. FILE *fp;
  536. Debug("Writing login info\n");
  537. if ((fp = fopen (filename, "w")) == 0 )
  538. return 0;
  539. if (verify->prpwd->uflg.fg_slogin)
  540. slogin=verify->prpwd->ufld.fd_slogin;
  541. else
  542. slogin=(time_t)0;
  543. if (verify->prpwd->uflg.fg_ulogin)
  544. ulogin=verify->prpwd->ufld.fd_ulogin;
  545. else
  546. ulogin=(time_t)0;
  547. if (verify->prpwd->uflg.fg_suctty)
  548. sterminal=verify->prpwd->ufld.fd_suctty;
  549. else
  550. sterminal="UNKNOWN";
  551. if (verify->prpwd->uflg.fg_unsuctty)
  552. uterminal=verify->prpwd->ufld.fd_unsuctty;
  553. else
  554. uterminal="UNKNOWN";
  555. slabel = mand_ir_to_er(verify->sec_label_ir);
  556. fprintf(fp,"-----------------------------------\n");
  557. fprintf(fp,"\nPrevious login information:\n\n");
  558. /* tricky formatting */
  559. if (slogin != 0) {
  560. sprintf(s, s1, ctime(&slogin));
  561. nl=strlen(s)-1;
  562. s[nl]='\0'; /* remove new-line */
  563. }else{
  564. sprintf(s,s1,"NEVER");
  565. }
  566. strcat(s, " from ");
  567. strncpy(term, sterminal, 14);
  568. term[14]='\0';
  569. strcat(s, term);
  570. fprintf(fp,"%s\n",s);
  571. if (ulogin != 0) {
  572. sprintf(s, s2, ctime(&ulogin));
  573. nl=strlen(s)-1;
  574. s[nl]='\0'; /* remove new-line */
  575. }else{
  576. sprintf(s,s2,"NEVER");
  577. }
  578. strcat(s, " from ");
  579. strncpy(term, uterminal, 14);
  580. term[14]='\0';
  581. strcat(s, term);
  582. fprintf(fp,"%s\n",s);
  583. label = (char*)malloc(strlen(message)+strlen(slabel)+1);
  584. sprintf(label, "%s%s", message, slabel);
  585. if (strlen (label) > 77) {
  586. for(i=75; label[i]!=',' && i>0; i--);
  587. if (i==0) for(i=75; label[i]!=' ' && i>0; i--);
  588. if (i==0) i=75;
  589. strncpy(s, label, i+1);
  590. s[i+1]='\0';
  591. fprintf(fp,"%s\n",s);
  592. strncpy(s, &label[i+1], 75);
  593. s[75]='\0';
  594. fprintf(fp,"%s\n",s);
  595. }else{
  596. fprintf(fp,"%s\n",label);
  597. }
  598. fclose(fp);
  599. return 1;
  600. }
  601. #endif /* BLS */